[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;
}