9.3练习题7 子串乘积正负分类 题解

题目出处:CF 1215B

题目描述

给你一个序列包含 \(n\) 个元素的序列 \(a_1, a_2, \dots , a_n\) (每个元素 \(a_i \ne 0\))。
你需要计算如下两个值:

  1. 有多少对数 \((l, r) (l \le r)\) 满足 \(a_l \cdot a_{l + 1} \dots a_{r - 1} \cdot a_r\) 的结果为正;
  2. 有多少对数 \((l, r) (l \le r)\) 满足 \(a_l \cdot a_{l + 1} \dots a_{r - 1} \cdot a_r\) 的结果为负。

即:这个序列中有多少子串(子串即连续子序列)的乘积为正,有多少子串的乘积为负。

输入格式

输入的第一行包含一个整数 \(n (1 \le n \le 2 \cdot 10^{5})\) —— 用于表示序列中元素的个数。
输入的第二行包含 \(n\) 个整数 \(a_1, a_2, \dots , a_n (-10^{9} \le a_i \le 10^{9}; a_i \neq 0)\) ,用于表示序列中的元素。

输出格式

输出两个正数,以一个空格分隔。分别表示乘积为正的子串的个数,以及乘积为负的子串的个数。

样例输入1

5
5 -3 3 -1 1

样例输出1

8 7

样例输入2

10
4 2 -4 3 1 2 -4 3 2 3

样例输出2

28 27

样例输入3

5
-1 -2 -3 -4 -5

样例输出3

9 6

问题分析

本体涉及算法:动态规划。
这道题目是一个动态规划入门题。
我们定义状态:

  • \(f[i][0]\) 表示以 \(a_i\) 结尾的乘积为正的子串个数;
  • \(f[i][1]\) 表示以 \(a_i\) 结尾的乘积为负的子串个数。

可以得到状态转移方程为:

  • \(a_i \lt 0\) 时:
    • \(f[i][0] = f[i-1][1]\)
    • \(f[i][1] = f[i-1][0] + 1\)
  • \(a_i \gt 0\) 时:
    • \(f[i][0] = f[i-1][0] + 1\)
    • \(f[i][1] = f[i-1][1]\)

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200020;
int n, a[maxn];
long long f[maxn][2], sum[2];   // f[i][0]表示以a[i]结尾整数数量, meanwhile f[i][2]负数。
int main() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    for (int i = 1; i <= n; i ++) {
        if (a[i] > 0) {
            f[i][0] = f[i-1][0] + 1;
            f[i][1] = f[i-1][1];
        }
        else { // if (a[i] < 0) {
            f[i][0] = f[i-1][1];
            f[i][1] = f[i-1][0] + 1;
        }
        for (int j = 0; j < 2; j ++) sum[j] += f[i][j];
    }
    cout << sum[1] << " " << sum[0] << endl;
    return 0;
}
posted @ 2019-09-23 10:04  zifeiynoip  阅读(426)  评论(0编辑  收藏  举报