[USACO]奶牛抗议(DP+树状数组+离散化)

Description

约翰家的N头奶牛聚集在一起,排成一列,正在进行一项抗议活动。第i头奶牛的理智度 为Ai,Ai可能是负数。约翰希望奶牛在抗议时保持理性,为此,他打算将所有的奶牛隔离成 若干个小组,每个小组内的奶牛的理智度总和都要大于零。由于奶牛是按直线排列的,所以 一个小组内的奶牛位置必须是连续的。

请帮助约翰计算一下,存在多少种不同的分组的方案。由于答案可能很大,只要输出答 案除以1,000,000,009的余数即可。

Solution

容易想到设\(F[i]\)表示 到第头\(i\)奶牛的方案数,

那么就有\(F[i]=\sum F[j](sum[i]-sum[j] \geq 0)\), sum为前缀和,

但如果直接枚举时间大概为\(O(n^2)\),显然会超时,那么考虑优化

式子可化为\(F[i]=\sum F[j](sum[i]\geq sum[j])\) ,

可以用前缀和为下标,构造树状数组维护小于等于\(sum[i]\)\(F\) 的和

那么前缀和很大,但是中间有很多没用的,离散化即可

注意\(F[0]=1\)要提前预处理进树状数组,时间复杂度\(O(nlogn)\)

Code

#include <cstdio>
#include <algorithm>
#define LL long long
#define N 1000010
using namespace std;

struct info {
    int id;
    LL sum;
} a[N];
const int yh = 1e9 + 9;
int n, t, p[N];
LL Ans, f[N];

int lowbit(int x) {
    return x & (-x);
}

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

bool cmp(info a, info b) {return a.sum < b.sum;}

void add(int x, LL v) {
    while (x <= n) {
        f[x] = (f[x] + v) % yh;
        x += lowbit(x);
    }
}

LL cal(int x) {
    LL r = 0;
    while (x) {
        r = (r + f[x]) % yh;
        x -= lowbit(x);
    }
    return r;
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++i) {
        a[i].sum = a[i - 1].sum + read();
        a[i].id = i;
    }
    a[n + 1].id = n + 1;    //F[0]=1
    a[n + 1].sum = 0;
    sort(a + 1, a + n + 2, cmp);
    for (int i = 1; i <= n + 1; i++) {  //离散化
        if (i == 1 || a[i].sum != a[i - 1].sum) ++t;
        p[a[i].id] = t;
    }
    add(p[n + 1], 1);
    for (int i = 1; i <= n; ++i) {
        Ans = cal(p[i]);
        add(p[i], Ans);
    }
    printf("%lld\n", Ans);
    return 0;
}

转载于:https://www.cnblogs.com/void-f/p/7736798.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值