【USACO】奶牛抗议 树状数组+dp
题目描述
约翰家的 N 头奶牛正在排队游行抗议。一些奶牛情绪激动,约翰测算下来,排在第 i 位的奶牛
的理智度为 A i ,数字可正可负。
约翰希望奶牛在抗议时保持理性,为此,他打算将这条队伍分割成几个小组,每个抗议小组的理
智度之和必须大于或等于零。奶牛的队伍已经固定了前后顺序,所以不能交换它们的位置,所以分在
一个小组里的奶牛必须是连续位置的。除此之外,分组多少组,每组分多少奶牛,都没有限制。
约翰想知道有多少种分组的方案,由于答案可能很大,只要输出答案除以 1000000009 的余数即
可。
输入
• 第一行:单个整数 N,1 ≤ N ≤ 100000
• 第二行到第 N + 1 行:第 i + 1 行有一个整数 A i ,−10 5 ≤ A i ≤ 10 5
输出
• 单个整数:表示分组方案数模 1000000009 的余数
样例输入
4 2 3 -3 1
样例输出
4
提示
如果分两组,可以把前三头分在一组,或把
后三头分在一组;如果分三组,可以把中间两头
分在一组,第一和最后一头奶牛自成一组;最后
一种分法是把四头奶牛分在同一组里。
题解:
朴素做法 if(sum[i]-sum[j]>=0)f[i]+=f[j].
于是发现只要sum[j]<=sum[i] 即可转移
然后以sum[i]为下标,维护树状数组即可,没事可以离散化一下.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long ll; 7 const int mod=1000000009,N=100005; 8 int gi(){ 9 int str=0,f=1;char ch=getchar(); 10 while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} 11 while(ch>='0' && ch<='9')str=str*10+ch-48,ch=getchar(); 12 return str*f; 13 } 14 int a[N],id[N],n;ll Tree[N*4],sum[N],b[N],f[N]; 15 int pf(ll x) 16 { 17 int l=1,r=n,mid; 18 while(l<=r) 19 { 20 mid=(l+r)>>1; 21 if(b[mid]==x)return mid; 22 if(x>b[mid])l=mid+1; 23 else r=mid-1; 24 } 25 return 0; 26 } 27 void add(int sta,ll x){for(int i=sta;i<=n;i+=(i&(-i)))Tree[i]+=x,Tree[i]%=mod;} 28 ll getsum(int sta) 29 { 30 ll sum=0; 31 for(int i=sta;i>=1;i-=(i&(-i)))sum+=Tree[i],sum%=mod; 32 return sum; 33 } 34 int main() 35 { 36 n=gi(); 37 for(int i=1;i<=n;i++)a[i]=gi(),sum[i]=sum[i-1]+a[i],b[i]=sum[i]; 38 sort(b+1,b+n+1); 39 for(int i=1;i<=n;i++) 40 { 41 id[i]=pf(sum[i]); 42 } 43 for(int i=1;i<=n;i++) 44 { 45 f[i]=getsum(id[i]); 46 if(sum[i]>=0)f[i]++; 47 f[i]%=mod; 48 add(id[i],f[i]); 49 } 50 printf("%lld",f[n]%mod); 51 return 0; 52 }