首页 > 代码库 > 【树状数组+dp+离散化】Counting Sequences

【树状数组+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 }
View Code

 

【树状数组+dp+离散化】Counting Sequences