[USACO11FEB]Generic Cow Protests

题目传送门

一道挺好的数据结构优化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\n",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\n",ans%mod);//记得取模 
	return 0;
}

完结撒花,欢迎大佬爆锤我。

posted @ 2020-06-26 22:24  qzwer  阅读(125)  评论(3编辑  收藏  举报