奶牛抗议 DP 树状数组
USACO的题太猛了
容易想到\(DP\),设\(f[i]\)表示为在第\(i\)位时方案数,转移方程:
\[
f[i]=\sum f[j]\;(j< i,sum[i]-sum[j]\ge0)
\]
\(O(n^2)\)过不了,考虑优化
移项得:
\[
f[i]=\sum f[j]\;(j< i,sum[i]\ge sum[j])
\]
这时候我们发现相当于求在\(i\)前面并且前缀和小于\(sum[i]\)的所有和,这就可以用一个树状数组优化了,在树状数组维护下标为\(sum[i]\),\(f[i]\)的前缀和。对于每个\(f[i]\)即为树状数组上\(sum[i]\)的前缀和。
这里需要注意的是前缀和可能为负,而树状数组下标不能为负,所以我们要离散化一下。
#include <cstdio> #include <algorithm> using namespace std; #define MAXN 100010 #define lowbit(x) ((x)&(-(x))) #define MOD 1000000009 int n,sum[MAXN],s; int sum_sort[MAXN+1]; int tre[MAXN+1]; inline void add(int x, int val){ while(x<=s){ tre[x]=(tre[x]+val)%MOD; x+=lowbit(x); } } inline int get_sum(int x){ int res=0; while(x>0){ res=(res+tre[x])%MOD; x-=lowbit(x); } return res; } int main(){ scanf("%d", &n); for(int i=1;i<=n;++i) scanf("%d", &sum[i]),sum[i]+=sum[i-1]; for(int i=1;i<=n;++i) sum_sort[i]=sum[i]; sort(sum_sort, sum_sort+1+n); s=unique(sum_sort, sum_sort+1+n)-sum_sort; for(int i=0;i<=n;++i) sum[i]=lower_bound(sum_sort, sum_sort+s, sum[i])-sum_sort+1; add(sum[0], 1); // f[0]=1 计数dp初始化 int ans=0; for(int i=1;i<=n;++i){ ans=get_sum(sum[i]); // 获得f[i] add(sum[i], ans); // 维护树状数组 } printf("%d\n", ans); return 0; }