題解
題目描述
人品是必不可少的,人品還是守恆的。每個人的人品都是不同的,並且有正的(選擇題可以用骰子全過),也有負的。
海亮高級中學有n 個學生,第i個人的人品值是
海亮高級中學決定重新規劃寢室,要讓每個寢室的人品和不小於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行,表示每個學生的人品 .
題解
我們先搞這道題的樸素dp
狀態
是前i個人的人品累加和。
這個狀態比較好做 代表前i個人的分配方式,
所以有
優化
所以只要用樹狀數組弄出所有的 就行了。
不過要記得離散化。
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;
}