日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDU - 5919 Sequence II——主席树+区间种类++逆序建树

發布時間:2023/11/30 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU - 5919 Sequence II——主席树+区间种类++逆序建树 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【題目描述】
HDU - 5919 Sequence II

【題目分析】
題目給定一個數組,每次查詢一個區間,找出區間內不同數字的個數x,然后輸出按出現順序第x/2向上取整個數字的位置。

  • 按照要求,我們首先需要能夠找出給定區間不同的數字個數。
    首先,我們分析一個簡單一些的問題:對于右端點固定的區間,如何計算不同左區間內不同數字的個數。
    我們不妨用一個數組記錄cntcntcnt哪些位置出現了一個不同的數字,用sumsumsum數組進行維護cnt[1..l]cnt[1..l]cnt[1..l]的和(可以用線段樹或者樹狀數組),那么對于區間[l,r][l,r][l,r]內不同數字的個數就是sum[r]?sum[l?1]sum[r]-sum[l-1]sum[r]?sum[l?1]
    在從前往后進行添加的過程中,如果該數字在前面已經出現,就將前面的標記消除,在后面的位置進行標記,也就是說盡可能將標記后放。例如對于數組1,2,2,3,5,11,2,2,3,5,11,2,2,3,5,1維護以后的cntcntcnt數組就是0,0,1,1,1,10,0,1,1,1,10,0,1,1,1,1,這樣做的原因是我們假設的是右端點固定,對于重復的元素,在后面如果出現過前面就沒有必要標記。
    如果詢問是離線的,我們大可以先將詢問保存下來,然后從前往后加入數據的過程中不斷將對應的詢問答案保存(對應是指右端點相同),最后輸出就可以了。
    可是這個問題是強制在線的,所以我們必須使用主席樹進行可持久化。可是這種可持久化和以前的主席樹運用不同,因為在添加的過程中會將前面的標記消除,所以不同根節點的主席樹不在擁有可以互相加減的能力(加減的結果不再有意義)。然而在我們這個問題里面我們是不需要進行加減的。
  • 不同于求區間第K大的時候我們的主席樹維護的是值區間,即值在區間內的個數,這里根節點的1..n1..n1..n指的是數據范圍aiaiai的最大值。在這里我們的根節點的1..n1..n1..nnnn指的是數據的個數,就是題目中的nnn,標記的是該位置上出現了一個之前沒有出現過的數字。 因此對于每次詢問,我們訪問的版本里保存的就是實際的數組,直接計數就可以。而區間第K大就需要減去之前的版本才是該區間內的數的個數。
  • 題目要求的是數據第一次出現的位置,可是按照上面的想法進行建樹的話我們保存的是當前區間[1,i][1,i][1,i]數據最后一次出現的位置。很自然,我們應該進行逆序建樹,這樣的話我們保存的就是[i,n][i,n][i,n]區間內數據第一次出現的位置,對于需要查詢的區間[l,r][l,r][l,r],我們訪問第lll個版本的主席樹,就滿足題目要求啦。
    求出數字的個數后我們再除以2向上取整就是題目要求的k,然后在找出對應的位置,這里類似區間第K大
    【參考文獻】
    大佬博客1
    大佬博客2
    【AC代碼】
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> #include<set> #include<climits> #include<string> #include<cmath> #include<cstdlib>using namespace std;const int MAXN=200005; int a[MAXN]; int n,m; struct node {int ls,rs,cnt; }tree[MAXN*40]; int root[MAXN*40]; int b[MAXN]; int tot;void Insert(int &now,int pre,int x,int l,int r,int add) {int tmp=now; now=++tot;tree[now]=tmp?tree[tmp]:tree[pre];tree[now].cnt+=add;if(l==r) return;int mid=(l+r)>>1;if(x<=mid) Insert(tree[now].ls,tree[pre].ls,x,l,mid,add);else Insert(tree[now].rs,tree[pre].rs,x,mid+1,r,add); }int GetSum(int k,int l,int r,int L,int R) {if(l>=L && r<=R) return tree[k].cnt;int ret=0; int mid=(l+r)>>1;if(L<=mid) ret+=GetSum(tree[k].ls,l,mid,L,R);if(R>mid) ret+=GetSum(tree[k].rs,mid+1,r,L,R);return ret; }int query(int k,int l,int r,int x) {if(l==r) return l;int mid=(l+r)>>1;int tmp=tree[tree[k].ls].cnt;if(x<=tmp) query(tree[k].ls,l,mid,x);else query(tree[k].rs,mid+1,r,x-tmp); }int main() {int T,ans,l,r,tmp,sum;scanf("%d",&T);for(int Case=1;Case<=T;Case++){scanf("%d%d",&n,&m);memset(tree,0,sizeof(tree));memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(root,0,sizeof(root));tot=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=n;i>0;i--){if(b[a[i]]) Insert(root[i],root[i+1],b[a[i]],1,n,-1);Insert(root[i],root[i+1],i,1,n,1);b[a[i]]=i;}ans=0;printf("Case #%d:",Case);//測試 //printf("\n");for(int i=0;i<m;i++){scanf("%d%d",&l,&r);l=(l+ans)%n+1; r=(r+ans)%n+1;if(l>r) tmp=l,l=r,r=tmp;//printf("test: l=%d r=%d\n",l,r);sum=GetSum(root[l],1,n,l,r);//printf("test: sum=%d\n",sum);sum=(sum+1)/2;//printf("test: sum=%d\n",sum);ans=query(root[l],1,n,sum);printf(" %d",ans);}printf("\n");}return 0; }

總結

以上是生活随笔為你收集整理的HDU - 5919 Sequence II——主席树+区间种类++逆序建树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。