树状数组+dp+离散化Counting Sequences
Posted shulin~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组+dp+离散化Counting Sequences相关的知识,希望对你有一定的参考价值。
https://www.bnuoj.com/v3/contest_show.php?cid=9149#problem/G
【题意】
给定一个数组a,问这个数组有多少个子序列,满足子序列中任意两个相邻数的差(绝对值)都不大于d.
【思路】
首先,朴素的dp思想:
dp[i]为以a[i]结尾的子问题的答案,则dp[i]=sum(dp[k]),k<i&&|a[k]-a[i]|<=d
但是时间复杂度为O(n^2),会超时。
我们可以这样想:
如果数组a排好序后,dp[i]就是区间(a[i]-d,a[i]+d)的结果和(直接把a[i]加到原数组后面)
所以自然而然就想到了用树状数组,区间求和求出dp[i],然后单点修改dp[i]以备后用。
这样时间复杂度就变成了O(nlogn)
另外要注意原来是很稀疏的大数据,我们要离散化压缩状态(排序去重)
【Accepted】
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int mod=9901; 4 typedef long long ll; 5 int n,d,cnt; 6 int c[100010],a[100010]; 7 int h[100010]; 8 int lowbit(int x) 9 { 10 return x&(-x); 11 } 12 void change(int x,int y) 13 { 14 for(;x<=n;x+=lowbit(x)) 15 { 16 c[x]+=y; 17 c[x]%=mod; 18 } 19 } 20 int ask(int x) 21 { 22 int ans=0; 23 for(;x;x-=lowbit(x)) 24 { 25 ans+=c[x]; 26 ans%=mod; 27 } 28 return ans; 29 } 30 int main() 31 { 32 while(scanf("%d%d",&n,&d)!=EOF) 33 { 34 cnt=n; 35 for(int i=1;i<=n;i++) 36 { 37 scanf("%d",a+i); 38 h[i]=a[i]; 39 c[i]=0; 40 } 41 sort(h+1,h+cnt+1); 42 cnt=unique(h+1,h+cnt+1)-h-1; 43 int ans=0; 44 for(int i=1;i<=n;i++) 45 { 46 int fi=1; 47 int l=lower_bound(h+1,h+cnt+1,a[i]-d)-h; 48 int r=upper_bound(h+1,h+cnt+1,a[i]+d)-h-1; 49 int pos=lower_bound(h+1,h+cnt+1,a[i])-h; 50 fi+=(ask(r)-ask(l-1)+mod)%mod; 51 fi%=mod; 52 ans+=fi; 53 ans%=mod; 54 change(pos,fi); 55 } 56 ans=(ans-n%mod+mod)%mod; 57 printf("%d\n",ans); 58 } 59 }
以上是关于树状数组+dp+离散化Counting Sequences的主要内容,如果未能解决你的问题,请参考以下文章
HDU 5862 Counting Intersections (离散化+扫描线+树状数组)
HDU 5542 - The Battle of Chibi - [离散化+树状数组优化DP]
Increasing Speed Limits HDU - 3030 dp 树状数组 离散化 上升子序列