题目
Description
约翰家的N 头奶牛正在排队游行*。一些奶牛情绪激动,约翰测算下来,排在第i 位的奶牛的理智度为Ai,数字
可正可负。约翰希望奶牛在*时保持理性,为此,他打算将这条队伍分割成几个小组,每个*小组的理智度之
和必须大于或等于零。奶牛的队伍已经固定了前后顺序,所以不能交换它们的位置,所以分在一个小组里的奶牛必
须是连续位置的。除此之外,分组多少组,每组分多少奶牛,都没有限制。约翰想知道有多少种分组的方案,由于
答案可能很大,只要输出答案除以1000000009 的余数即可。
Input
第一行:单个整数N,1 ≤ N ≤ 100000
第二行到第N + 1 行:第i + 1 行有一个整数Ai,10^5 ≤ Ai ≤ 10^5
Output
单个整数:表示分组方案数模1000000009 的余数
Sample Input
4 2 3 -3 1
Sample Output
4
Hint
说明
解释:如果分两组,可以把前三头分在一组,或把后三头分在一组;
如果分三组,可以把中间两头分在一组,第一和最后一头奶牛自成一组;最后一种分法是把四头奶牛分在同一组里。
思路:
如果不知道怎么求a前面小于a的个数
https://www.cnblogs.com/wzx-RS-STHN/p/13193271.html
暴力
首先我们先把这一题的dp思路想清;
dp[i]代表1-i的分组方案数;s[i]代表1-i的和;
如果j-i区间的和大于等于 0;dp[i]+=dp[j];
#include<bits/stdc++.h> typedef long long ll; using namespace std; inline ll read() { ll a=0,ha=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') ha=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*ha; } const ll N=5*1e5; const ll mod=1000000009; ll s[N],ans,n,a[N],dp[N]; int main() { n=read(); for(ll i=1;i<=n;i++) { a[i]=read(); s[i]=s[i-1]+a[i]; } dp[0]=1; for(ll i=1;i<=n;i++) for(ll j=0;j<i;j++) { if(s[i]>=s[j])//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0 dp[i]=(dp[i]+dp[j])%mod; } printf("%lld\n",dp[n]); }
正解
那么怎么优化呢?树状数组!!!
转移方程
for(ll j=0;j<i;j++) { if(s[i]>=s[j])//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0 dp[i]=(dp[i]+dp[j])%mod; }
很容易想到,这一段代码其实相当于求,s[i]前面比s[i]小的s[j]有多少个;dp[i]+=dp[j];
ll x=findout(b[i]);//找出前面有多少数比b[i]小,并把那些位置上的方案数加上; ans=x%mod; insert(b[i],ans);//将ans加入树状数组相当于暴力代码的记录dp[i]
b[i]是s[i]的离散化;
通过树状数组优化转移方程就ok了;
代码:
#include<bits/stdc++.h> typedef long long ll; using namespace std; inline ll read() { ll a=0,ha=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') ha=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*ha; } const ll mod=1000000009; ll n,m,sum[1000010]; ll b[1000010],a1[1000010],s[1000010]; struct oh { ll v,num; }a[1000010]; inline ll lowbit(ll x)//树状数组基本操作 { return x&(-x); } inline void insert(ll x,ll y) { while(x<=n+1) { sum[x]+=y; x+=lowbit(x); } } inline ll findout(ll x) { ll ans=0; while(x) { ans+=sum[x]; x-=lowbit(x); } return ans; } inline ll cmp(oh a,oh b) { if(a.v==b.v) return a.num<b.num; else return a.v<b.v; } int main() { n=read(); for(ll i=1;i<=n;i++) { a1[i]=read(); s[i+1]=s[i]+a1[i];//记录前缀和 } s[1]=0;//边界相当于dp[0]=1 for(ll i=1;i<=n+1;i++) { a[i].v=s[i]; a[i].num=i; }//离散化 sort(a+1,a+n+2,cmp); for(ll i=1;i<=n+1;i++) b[a[i].num]=i; //代码修改艰难痕迹 // cout<<endl; // for(ll i=1;i<=n+1;i++) // cout<<b[i]<<endl; insert(b[1],1); ll ans=0; for(ll i=2;i<=n+1;i++) { ll x=findout(b[i]);//找出前面有多少数比b[i]小,并把那些位置上的方案数加上; ans=x%mod; insert(b[i],ans);//将ans加入树状数组相当于暴力代码的记录dp[i] } printf("%lld\n",ans); }
//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0