P2344 奶牛抗议 离散化+前缀和+动态规划+树状数组

【题目背景】

Generic Cow Protests, 2011 Feb

【题目描述】

约翰家的N 头奶牛正在排队游行抗议。一些奶牛情绪激动,约翰测算下来,排在第i 位的奶牛的理智度为Ai,数字可正可负。

约翰希望奶牛在抗议时保持理性,为此,他打算将这条队伍分割成几个小组,每个抗议小组的理智度之和必须大于或等于零。奶牛的队伍已经固定了前后顺序,所以不能交换它们的位置,所以分在一个小组里的奶牛必须是连续位置的。除此之外,分组多少组,每组分多少奶牛,都没有限制。

约翰想知道有多少种分组的方案,由于答案可能很大,只要输出答案除以$1000000009$ 的余数即可。

【输入格式】

• 第一行:单个整数N,$1 ≤ N ≤ 100000$

• 第二行到第N + 1 行:第i + 1 行有一个整数Ai,$−10^5 ≤ Ai ≤ 10^5$

【输出格式】

单个整数:表示分组方案数模$1000000009$ 的余数

【输入样例】
4
2
3
-3
1
【输出样例】 
4  

说明:

解释:如果分两组,可以把前三头分在一组,或把后三头分在一组;如果分三组,可以把中间两头分在一组,第一和最后一头奶牛自成一组;最后一种分法是把四头奶牛分在同一组里。

题解:

记录前缀理智和sum[i],l ~ r 可分成一组 当且仅当sum[r] >= sum[l-1];

令dp[i]表示以i作为某一段的结尾的分组方式。dp[n]就是要求的答案

dp[i]=∑dp[j] ……j<i且sum[j]<=sum[i];

<=sum[i]的dp[j]的和可以使用树状数组维护。

因此,先以sum[i]离散化,每次动态查询,动态修改

特别的 : dp[0] = 1,优先插入;

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e5 + 5, mod = 1e9 + 9;
typedef long long LL;
int n, k = -1, sum[N], b[N], c[N];
int lowbit(int x) { return x & -x; }
void add(int x, int k) {
    for( ;x <= n + 5; x += lowbit(x)) (c[x] += k) %= mod;
}
int getsum(int x) {
    int res = 0;
    for( ; x ;x -= lowbit(x)) (res += c[x]) %= mod;
    return res;
}
int main () {
    cin >> n;
    for(int i = 1;i <= n;i ++) 
        cin >> sum[i], sum[i] += sum[i-1], b[i] = sum[i];
    sort(b + 1, b + n + 1);
    for(int i = 0;i <= n;i ++) 
        sum[i] = lower_bound(b+1, b+n+1, sum[i]) - b + 2; 
    LL ans = 0;
    add(sum[0], 1);
    for(int i = 1;i <= n;i ++) {
        ans = getsum(sum[i]);
        add(sum[i], ans);
    }
    cout << ans << endl;
    return 0;
}

 

posted @ 2019-07-16 21:50  Paranoid丶离殇  阅读(204)  评论(0编辑  收藏  举报