CodeForce 1215B 题解

题目出处:Problem - B - Codeforces

题目描述:

给你一个序列包含 n 个元素的序列 a1,a2,…,an (每个元素 ai≠0)。
你需要计算如下两个值:

  1. 有多少对数 (l,r)(l≤r) 满足 al⋅al+1…ar−1⋅ar 的结果为正;
  2. 有多少对数 (l,r)(l≤r) 满足 al⋅al+1…ar−1⋅ar 的结果为负。

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

输入格式

输入的第一行包含一个整数 n(1≤n≤2⋅105) —— 用于表示序列中元素的个数。
输入的第二行包含 n 个整数 a1,a2,…,an(−109≤ai≤109;ai≠0) ,用于表示序列中的元素。

输出格式

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

样例输入1:

5
5 -3 3 -1 1

样例输出:

8 7

样例输入2:

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

样例输出:

28 27

问题分析:

本题使用动态规划来求解,我们定义状态:

f[i][0] 表示以a[i]结尾(或者说包含a[i])的乘积为正的字串个数;

f[i][1] 表示以a[i]结尾(或者说包含a[i])的乘积为正的字串个数;

注意了,这里说的以a[i]结尾的乘积为正的字串个数,指的不是说从a0到ai这个序列的子序列中乘积为正的子序列的个数,

而是说,这些子序列必须包含a[i],必须以a[i]结尾。

我们可以得出状态转移方程:

  • 当ai < 0时:
    • f[i][0] = f[i-1][1]
    • f[i][1] = f[i-1][0] + 1
  • 当ai > 0 时:
    • f[i][0] = f[i-1][0] +1
    • f[i][1] = f[i-1][1]

首先再强调一下,f[i][0]所对应的字串必须以a[i]结尾,

  • 当 ai < 0 时,其实f[i][0]就是 以ai-1结尾的乘积为负数的各个字串加上尾部数字ai组成的各个新字串,负负得正,这样乘积重新变成正数了,所以就是f[i][0] = f[i-1][1];
  • 同理,f[i][1] 就是以ai-1结尾的乘积为正数的各个字串 加上尾部数字ai组成的各个新字串,再加上ai这个负数本身,所以就是f[i][1] = f[i-1][0] + 1;
  • 当ai > 0 时,f[i][0]就是 以ai-1结尾的乘积为正数的各个字串加上尾部数字ai组成的各个新字串,正数和正数相乘还是正数,同时再加上ai这个正数本身,所以就是f[i][0] = f[i-1][0] +1
  • 同理,f[i][1] 就是以ai-1结尾的乘积为负数的各个字串 加上尾部数字ai组成的各个新字串,所以就是f[i][1] = f[i-1][1]。

再举例子来说明,以样例数据为例

5
5 -3 3 -1 1

正  负     序列
1   0      5    序列只有5,乘积为正的是1个,乘积为负的是0个
0   2      5 -3  序列加入-3,以-3结尾的乘积为正的子序列是0个 
1   2      5 -3 3  序列加入3,以3结尾的乘积为正的子序列是1个
2   2      5 -3 3 -1 
3   2      5 -3 3 -1 1

我们以一种从后往前的思路的去推导,每增加一个ai,就考虑ai和前面数据的关系,当然这种动态规划的思想确实有点难度,需要多练习。

程序如下:

#include <bits/stdc++.h>
using namespace std;
const int maxnum = 5001;
int n,a[maxnum];
long long f[maxnum][2], res1=0,res2=0;
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 {
            f[i][0] = f[i-1][1];
            f[i][1] = f[i-1][0] + 1;
        }

        res1 += f[i][0];
        res2 += f[i][1];

    }
    cout << res1 << " and "<< res2 <<endl;
    return 0;
}

同时这道题也有一个变种,比如说输入的只有1 和 -1 两种数字,判断乘积为正的子序列的个数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值