题解
题目描述
人品是必不可少的,人品还是守恒的。每个人的人品都是不同的,并且有正的(选择题可以用骰子全过),也有负的。
海亮高级中学有n (1<=n<=100,000)(1<=n<=100,000)个学生,第i个人的人品值是Ai(−10,000<=Ai<=10,000).Ai(−10,000<=Ai<=10,000).
海亮高级中学决定重新规划寝室,要让每个寝室的人品和不小于0.寝室的人品和是这个寝室里所有人的人品和。
因为名单是按照学籍号给出的,所以,现在你不能调整学生的顺序,只能按照输入的顺序把学生依次分进各个寝室,当然,为了增加难度,每个寝室的人数可以不同,我们甚至可以把n个学生放在同一个寝室。我们的口号,人品比宿舍重要。
比如现在有4个人,人品分别是2, 3, -3, 1。我们可以按照下面分寝室:
(2 3 -3 1)
(2 3 -3) (1)
(2) (3 -3 1)
(2) (3 -3) (1)
这样保证每个寝室的人品和不小于0.
现在的问题是,如果按照这种原则,有多少种分寝室的方法。当然,方法可能很多,你只需要输出方案数对 1,000,000,009 取余数即可。
输入格式
第一行一个整数n
接下来n行,表示每个学生的人品AiAi.
题解
我们先搞这道题的朴素dp
状态
s[i]s[i]是前i个人的人品累加和。
这个状态比较好做f[i]f[i]代表前i个人的分配方式,
所以有f[i]=∑f[j](s[j]<=s[i],j<=i)且有f[0]=1f[i]=∑f[j](s[j]<=s[i],j<=i)且有f[0]=1
优化
所以只要用树状数组弄出所有的∑f[j]∑f[j]就行了。
不过要记得离散化。
code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int num=0;char c=' ';bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=(num<<3)+(num<<1)+c-48,c=getchar());
return flag ? num : -num;
}
const int maxn=100020,p=1000000009;
int n;
struct S{
int v,id;
}s[maxn];
bool mycmp(S a,S b){
return a.v<b.v;
}
void init(){
n=read();
for(int i=1;i<=n;i++){
s[i].v=read()+s[i-1].v;
s[i].id=i;
}
}
int pos[maxn];
void LSH(){
sort(s,s+n+1,mycmp);
int cnt=1,t=0;
for(int i=0;i<=n;i++)
if(s[i].v==s[t].v)pos[s[i].id]=cnt;
else{
pos[s[i].id]=++cnt;
t=i;
}
}//离散化
namespace shuzhuangshuzu{
int lowbit(int t){return t&-t;}
int ans,d[maxn];
int find(int x){
int t=0;
while(x){
t+=d[x];
x-=lowbit(x);
t%=p;
}
return t;
}//查询和
void updata(int x,int v){
while(x<=n){
d[x]+=v;
d[x]%=p;
x+=lowbit(x);
}
}//更新数据,x的位置加上v。
}using namespace shuzhuangshuzu;
void dp(){
updata(pos[0],1);
for(int i=1;i<=n;i++){
ans=find(pos[i]);
updata(pos[i],ans);
}
}
int main(){
init();
LSH();
dp();
printf("%d\n",ans);
return 0;
}