HDU 3450 Counting Sequences(樹狀陣列+離散化+二分+dp思想 詳細解答)
阿新 • • 發佈:2018-12-09
題意:給出一段序列,讓你找出一段子序列(長度>=2)滿足序列中每相鄰的兩個數之間的差 <=d,求出這樣的序列的個數。
思路:本題利用dp思想,dp[i] 表示 以 i 為結尾的子序列個數(主要是記錄)
以1 3 7 5 為例:
1 2 3 4 5 6 7
1 |
先將1加入,計算dp[3]=val[2]+dp[2]+val[1]+dp[1]+val[4]+dp[4]+val[5]+dp[5](val 代表當前的值,長度d=2,區間為[3-2,3+2])
結束再將3加入
1 2 3 4 5 6 7
1 | 1 | 1 |
比如加入5時,dp[5]=val[3]+dp[3]+val[7]=3;
但是本題因為沒有資料範圍,需要離散化處理,處理起來比較麻煩。我們考慮把val[i]和dp[i]合併成新的dp[i],
因此計算區間[n-d,n+d]時只需要計算區間上 dp的和,此時考慮使用樹狀陣列來求和。
程式碼中詳細解釋:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100000+10; const int mod=9901; int a[maxn],dp[maxn],b[maxn],sum[maxn]; int n; int lower_bit(int x) { return x&(-x); } void update(int pos,int val) { while(pos<=n) { sum[pos]+=val; pos+=lower_bit(pos); } } int query(int pos) { int ans=0; while(pos>0) { ans+=sum[pos]; pos-=lower_bit(pos); } return ans; } int main() { int d; while(~scanf("%d%d",&n,&d)) { memset(sum,0,sizeof(sum)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); unique(b+1,b+n+1);//對b 陣列 去重 int ans=0; for(int i=1;i<=n;i++) { //利用lower_bound進行離散化 int pos1=upper_bound(b+1,b+n+1,a[i]+d)-(b+1);//找到右界 int pos2=lower_bound(b+1,b+n+1,a[i]-d)-(b);//找到左界 int temp=lower_bound(b+1,b+n+1,a[i])-b;//當前元素位置 int sum=((query(pos1)-query(pos2-1))%mod+mod)%mod;//求和,sum相當於dp[i] 記錄當前區間【n-d,n+d】 上符合的序列個數 ans=(ans+sum)%mod;//求出所有dp【i】的和 update(temp,sum+1);//sum+1 ,就是將dp[i]與val[i] 合併 } printf("%d\n",ans); } return 0; }
程式碼中對pos1使用upper_bound考慮樣例:1 3 7 5,在對5+2查詢時找到位置5,-1處理對應7所在位置4,對應區間[3,7]符合題意;1 3 8 5,對5+2查詢到位置5(對應數字8),數字8不包括在區間內,-1處理滿足題意,算是個小技巧吧!