hdu 2836 树状数组 + DP

2A的题,因为注释没去完。。。。。。

题意:给你n个数,求长度大于2的且相邻数字绝对值差不超过H的序列的个数

很容易想到一个朴素的O(n^2)DP

dp[i]=sigma(dp[j],abs(val[i]-val[j])<=H)

由于DP转移过程是求和的操作,所以可以用树状数组来优化求和操作

进一步转换:val[i]-H<=val[j]<=val[i]+H,所以每次把val[i]-H到val[i]+H之间的树状数组中的数求和  假设和为pre,dp[i]=pre+1;表示以i结尾的序列的个数

序列长度>=1,求和后再把dp[i]放进树状数组即可

最后的答案还要减掉n,取出长度为1的序列,代码还算简洁,因为少写了好几个二分,都用STL中的lower_bound upper_bound代替掉了

View Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int mod=9901;
int n,H;
int c[maxn],num[maxn],val[maxn],sum;
void update(int x,int d){
for(;x<maxn;x+=x&-x){
c[x]+=d;if(c[x]>mod) c[x]%=mod;
}
}
int query(int x){
for(sum=0;x>0;x-=x&-x){
sum+=c[x];if(sum>mod) sum%=mod;
}
return sum;
}
int main(){
int i;
while(scanf("%d%d",&n,&H)!=EOF){
memset(c,0,sizeof(c[0])*(n+3));
for(i=1;i<=n;i++){
scanf("%d",&num[i]);
val[i]=num[i];
}
sort(num+1,num+n+1);
int tot=unique(num+1,num+n+1)-num-1;
int ans=0;
for(i=1;i<=n;i++){
int id=lower_bound(num+1,num+tot+1,val[i])-num;
int l=lower_bound(num+1,num+tot+1,val[i]-H)-num;
int r=upper_bound(num+1,num+tot+1,val[i]+H)-num-1;
int pre=(query(r)-query(l-1))%mod;
if(pre<0) pre+=mod;
ans+=pre+1;
update(id,pre+1);
}
printf("%d\n",((ans-n)%mod+mod)%mod);
}
}



你可能感兴趣的:(树状数组)