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
说明
解释:如果分两组,可以把前三头分在一组,或把后三头分在一组;如果分三组,可以把中间两头分在一组,第一和最后一头奶牛自成一组;最后一种分法是把四头奶牛分在同一组里。
Solution
用f[i]表示前I头cows的分组方案数,很容易得到DP方程:
边界
然后sum部分可以用前缀和维护,朴素的DP 大约是所以会TLE两个CASES
1 const p=1000000009; 2 var 3 n,i,j:longint; 4 a,s,f:array[0..100000] of longint; 5 6 function sum(i,j:longint):longint; 7 begin 8 exit(s[j]-s[i]); 9 end; 10 11 procedure add(var app:longint; aa:longint); 12 begin 13 app:=((app mod p)+(aa mod p)) mod p; 14 end; 15 16 begin 17 readln(n); 18 for i:= 1 to n do 19 begin 20 readln(a[i]); 21 s[i]:=s[i-1]+a[i]; 22 end; 23 24 f[0]:=1; 25 for i:= 1 to n do 26 for j:= 0 to i-1 do 27 if sum(j,i)>=0 then add(f[i],f[j]); 28 29 writeln(f[n]); 30 end.
于是我们要优化哇,对状态转移方程移项可得:
可见求f[i]时,如果前面的sum[j-1]小于等于sum[i]就把他对应的f[j]加到f[i]里
所以我们可以用树状数组维护,用sum作为tree的下标表示tree[b]表示当前情况下sum=b的对应的分组种数
因为sum=b可能会超级大,于是我们可以离散
离散,就是计算出第i个前缀和的排第几小存在f[i]里面,双字段qsort实现,因为要保证后面的循环查询更新按照顺序
但是要注意边界和负数情况,我们首先将Sum=0加入同时更新边界即 add(f[1],1)
然后循环不断查询tree[1..sum[i]]的区间和便得到了此时的f[i]存到ans, 并更新
那么时间复杂度大概是
1 program w; 2 const 3 p=1000000009; 4 5 var 6 n,i,ans:longint; 7 sum,a,rk,f,tree:array[0..100000] of longint; 8 9 function lowbit(app:longint):longint; 10 begin 11 exit(app and -app); 12 end; 13 14 procedure add(ii,x:longint); 15 begin 16 while ii<=n do 17 begin 18 tree[ii]:=(tree[ii]+x) mod p; 19 ii:=ii+lowbit(ii); 20 end; 21 end; 22 23 function get(aa:longint):longint; 24 begin 25 get:=0; 26 while aa>0 do 27 begin 28 get:=(get+tree[aa]) mod p; 29 aa:=aa-lowbit(aa); 30 end; 31 end; 32 33 procedure qsort(l,r: longint); 34 //注意一定要双字段 35 var 36 i,j,x,y,z: longint; 37 begin 38 i:=l; j:=r; x:=sum[(l+r) div 2]; z:=rk[(l+r) div 2]; 39 repeat 40 while (sum[i]<x) or ((sum[i]=x) and (rk[i]<z)) do inc(i); 41 while (x<sum[j]) or ((sum[j]=x) and (rk[j]>z)) do dec(j); 42 if not(i>j) then 43 begin 44 y:=sum[i]; sum[i]:=sum[j]; sum[j]:=y; 45 y:=rk[i]; rk[i]:=rk[j]; rk[j]:=y; 46 inc(i); j:=j-1; 47 end; 48 until i>j; 49 if l<j then qsort(l,j); 50 if i<r then qsort(i,r); 51 end; 52 53 begin 54 readln(n); 55 for i:= 1 to n do 56 readln(a[i]); 57 58 n:=n+1; sum[1]:=0; rk[1]:=1; 59 for i:= 2 to n do 60 begin 61 sum[i]:=sum[i-1]+a[i-1]; 62 rk[i]:=i; 63 end; 64 65 qsort(1,n); 66 67 for i:= 1 to n do 68 f[rk[i]]:=i; 69 70 add(f[1],1); //处理边界 71 for i:= 2 to n do //循环到i ,得出来的ans便是f[i]的值 72 begin 73 ans:=get(f[i]); 74 add(f[i],ans); 75 end; 76 77 writeln(ans); 78 end.