题目传送门
一道挺好的数据结构优化DP的题目,树状数组搞一搞就好了。
sol
讲下思路,首先看了眼数据范围 (1≤N≤10^5) ,暴力很好写吧,(O(n^2))的线性dp
for(int i=1;i<=n;i++)scanf("%d",&f[i]),s[i]=s[i-1]+f[i];//统计一下前缀和
dp[0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
if(s[i]-s[j]>=0)dp[i]+=dp[j],dp[i]%=mod;//线性dp
}
}
printf("%d
",dp[n]);//目标dp[n]
然后我们发现
前缀和数组(s_i),dp数组(f_i),最终答案为(f_n)
那么怎么计算答案呢,看下样例(其中(0≤i≤n))
(i) | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
(a_i) | 0 | 2 | 3 | -3 | 1 |
(s_i) | 0 | 2 | 5 | 2 | 3 |
因为数的范围为(-10^5 ≤ Ai ≤ 10^5),所以需要离散化一下防止RE,设离散化数组为(r_i),也就是每个(s_i)的排名
(i) | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
(r_i) | 1 | 2 | 4 | 2 | 3 |
因为要保证(s_i≥s_j)((1≤i≤n,0≤j<i)),(f_i)加上之前比他小的有多少方案(其实就是一个加法原理)
我们设值域为数的种类,建立一个树状数组,其中第(r_i)位表示这个数之前有多少个数小于等于他,画个图理解一下
当(i)=0时,直接加上
当(i)=1时,因为(r_1)=2,所以在第二个位置加入,又因为在第二个位置前有1个数加入,所以第二个位置的值为1
当(i)=2时,因为(r_2)=4,所以在第四个位置加入,又因为在第四个位置前有2个数加入,所以第二个位置的值为2
注意当(i)=3时,(r_3)=2,从前出现过,但还是一样,在第二个位置加入,先统计,再加入
因为第二个位置之前有2个数加入,所以在第二个位置加上2,故
当(i)=4时,以此类推,所以最后的答案为4
cpp
#include<bits/stdc++.h>
#define mod 1000000009
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int n;
int tree[maxn];
inline int lowbit(int x){return x&(-x);}//树状数组基本操作
void add(int x,int d){for(int i=x;i<=1e5+10;i+=lowbit(i))tree[i]+=d,tree[i]%=mod;}//添加操作 记得取模
inline int sum(int x){ll sum=0;for(;x;x-=lowbit(x))sum+=tree[x],sum%=mod;return sum;}//求和操作 记得取模
int s[maxn],f[maxn];//s为前缀和
ll ans;
int b[maxn];//离散化数组
int main(){
scanf("%d",&n);
for(int i=1,x;i<=n;i++)scanf("%d",&x),s[i]=s[i-1]+x,b[i]=s[i];//用离散化的数组记录起来
sort(b,b+n+1);
for(int i=0;i<=n;i++)s[i]=lower_bound(b,b+n+1,s[i])-b+1;//离散化
add(s[0],1);//s[0]方案数为1
for(int i=1;i<=n;i++){
ans=sum(s[i]);//查询在s[i]前有多少小于等于它的数
add(s[i],ans);//加入到树状数组中
}
printf("%lld
",ans%mod);//记得取模
return 0;
}
完结撒花,欢迎大佬爆锤我。