【NOI2019集訓題2】 序列 後綴樹+splay+dfs序

題目大意:給你一個長度爲$n$的序列$a_i$,還有一個數字$m$,有$q$次詢問c++

每次給出一個$d$和$k$,問你對全部的$a_i$都在模$m$意義下加了$d$後,第$k$小的後綴的起點編號。ui

數據範圍:$n≤100000,d≤a_i<m≤10^9,q≤5\times 10^5$spa

 

這一題我想的時候被最後一步卡主了(其實若是到那個時候估計也時間不夠了)code

咱們不難找出一個單次詢問$O(n)$的方法,咱們每次暴力更新$a_i$,而後對原序列搞一棵後綴樹出來,在上面暴力查詢第$k$小便可。blog

若是沒有加$d$的操做,只是單純詢問第k大的話,咱們考慮對這棵後綴樹按字典序先序遍歷一遍,搞出dfs序,用平衡樹維護這些點出現的前後順序。排序

對於一次詢問第$k$小,咱們在平衡樹上找出dfs序第$k$小的後綴節點出來,輸出便可。get

下面考慮加$d$的操做。it

 

考慮到這一題並無要求強制在線,咱們考慮對全部的詢問按$d$從小大到大排序。ast

隨着d的增加,原先最大的數隨着取模操做的發生,會變成最小的數。class

也就是說會出現一個換位的狀況

 

就像這樣

顯然這個紅點下面還會接不少個節點,可是無論怎麼換位,以紅點爲根的子樹內孩子的數量是不會變的。

咱們只須要不斷地作搬動節點的操做(在平衡樹中,取出一個區間,並把這個區間插入到另外一個區間中),而且動態維護dfs序列便可。

在詢問離線後,咱們不難發現每一個節點至多隻須要被搬動一次。

那麼時間複雜度就變成愉快的O((n+q)\ log\ n)了。

 

然而我在想的時候,並無往dfs序上想,sam也不熟練

注意細節!

  1 #include<bits/stdc++.h>
  2 #define M 200005
  3 #define lc(x) ch[(x)][0]
  4 #define rc(x) ch[(x)][1]
  5 using namespace std;
  6 
  7 int dfn[M]={0},low[M]={0},t=0; int n,m,q;
  8 
  9 namespace H{
 10     int ch[M][2]={0},fa[M]={0},root,siz[M]={0},psiz[M]={0},p[M]={0},rec[M]={0},use=0;
 11      
 12     void pushup(int x){siz[x]=siz[lc(x)]+siz[rc(x)]+1;psiz[x]=psiz[lc(x)]+psiz[rc(x)]+p[x];}
 13     void rotate(int x,int &k){
 14         int y=fa[x],z=fa[y],l,r;
 15         l=(ch[y][0]!=x); r=l^1;
 16         if(y==k) k=x;
 17         else{
 18             if(ch[z][0]==y) ch[z][0]=x;
 19             else ch[z][1]=x;
 20         }
 21         fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;
 22         ch[y][l]=ch[x][r]; ch[x][r]=y;
 23         pushup(y); pushup(x);
 24     }
 25     inline void splay(int x,int &k){
 26         while(x!=k){
 27             int y=fa[x],z=fa[y];
 28             if(y!=k){
 29                 if((ch[y][0]==x)^(ch[z][0]==y)) rotate(x,k);
 30                 else rotate(y,k);
 31             }
 32             rotate(x,k);
 33         }
 34     }
 35     int build(int l,int r,int f){
 36         if(l>r) return 0;
 37         int mid=(l+r)>>1; fa[mid]=f;
 38         lc(mid)=build(l,mid-1,mid); 
 39         rc(mid)=build(mid+1,r,mid);
 40         pushup(mid);
 41         return mid;
 42     }
 43     int find(int x,int k){
 44         if(siz[lc(x)]>=k) return find(lc(x),k);
 45         if(siz[lc(x)]+1==k) return x;
 46         return find(rc(x),k-siz[lc(x)]-1);
 47     }
 48     int findp(int x,int k){
 49         if(psiz[lc(x)]>=k) return findp(lc(x),k);
 50         if(psiz[lc(x)]+p[x]==k) return x;
 51         return findp(rc(x),k-psiz[lc(x)]-p[x]);
 52     }
 53     int getrank(int x){
 54         splay(x,root);
 55         return siz[lc(x)];
 56     }
 57     int split(int l,int r){
 58         int x=find(root,l),y=find(root,r+2);
 59         splay(x,root); splay(y,ch[root][1]);
 60         int res=lc(y);
 61         lc(y)=0;
 62         splay(y,root);
 63         return res;
 64     }
 65     void ins(int k,int id){
 66         int x=find(root,k+1);
 67         splay(x,root);
 68         int y=rc(x);
 69         while(lc(y)) y=lc(y);
 70         lc(y)=id; fa[id]=y;
 71         splay(lc(y),root);
 72     }
 73     void build(int nn){
 74         root=build(1,nn+1,0);
 75         splay(1,root);
 76         lc(1)=nn+2; siz[1]++; siz[nn+2]++;
 77     }
 78 };
 79 
 80 int a[M]={0};
 81 map<int,int> mp; vector<int> vt[M];
 82 
 83 namespace SAM{
 84     map<int,int> ch[M],son[M]; int l[M],fa[M],last=1,use=1,cnt=0;
 85     int pos[M],ed[M],val[M],siz[M];
 86     void exc(int c,int id){
 87         int p=last,np=++use; l[np]=l[last]+1; last=np;
 88         pos[np]=ed[np]=id;
 89         for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=np;
 90         if(!p) fa[np]=1;
 91         else{
 92             int q=ch[p][c];
 93             if(l[p]+1==l[q]) fa[np]=q;
 94             else{
 95                 int nq=++use;
 96                 l[nq]=l[p]+1; fa[nq]=fa[q];
 97                 fa[q]=fa[np]=nq; ed[nq]=ed[q];
 98                 ch[nq]=ch[q];
 99                 for(int j=p;ch[j][c]==q;j=fa[j]) ch[j][c]=nq;
100             }
101         }
102     }
103     
104     void build(){
105         for(int i=n;i;i--) 
106         exc(a[i],i);
107         for(int i=2;i<=use;i++){
108             val[i]=a[ed[i]+l[fa[i]]];
109             if(mp[val[i]]==0) mp[val[i]]=++cnt;
110             vt[mp[val[i]]].push_back(i);
111             son[fa[i]][val[i]]=i;
112         }
113     }
114     
115     void dfs(int x){
116         dfn[x]=++t; siz[x]=1;
117         H::rec[t]=x;
118         if(pos[x]) H::p[t]=1;
119         for(map<int,int>::iterator it=son[x].begin();it!=son[x].end();it++){
120             dfs(it->second);
121             siz[x]+=siz[it->second];
122         }
123         low[x]=t;
124     }
125     void updata(int ID){
126         for(int i=0;i<vt[ID].size();i++){
127             int id=vt[ID][i];
128             int F=fa[id];
129             int wei=H::getrank(dfn[F]);
130             int cutID=wei+siz[F]-siz[id];
131             int P=H::split(cutID,cutID+siz[id]-1);
132             
133             H::ins(wei,P);
134         }
135     }        
136 };
137 
138 struct ask{
139     int d,k,id;
140     ask(){d=k=id=0;}
141     void rd(int ID){id=ID; scanf("%d%d",&d,&k);}
142     friend bool operator <(ask a,ask b){return a.d<b.d;}
143 }Q[500005];
144 
145 int ans[500005]={0};
146 
147 int main(){
148 //    freopen("in.txt","r",stdin);
149 //    freopen("out.txt","w",stdout);
150     scanf("%d%d%d",&n,&m,&q);
151     for(int i=1;i<=n;i++) scanf("%d",a+i);
152     
153     SAM::build();
154     SAM::dfs(1);
155     H::build(SAM::use);
156     
157     for(int i=1;i<=q;i++) Q[i].rd(i);
158     sort(Q+1,Q+q+1);
159     sort(a+1,a+n+1);
160     int r=unique(a+1,a+n+1)-a-1,cnt=r;
161     
162     for(int i=1;i<=q;i++){
163         while(r&&a[r]+Q[i].d>=m){
164             if(mp[a[r]]) 
165             SAM::updata(mp[a[r]]);
166             r--;
167         }
168         int ID=H::findp(H::root,Q[i].k);
169         ans[Q[i].id]=SAM::pos[H::rec[ID]];
170     }
171     for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
172 }
相關文章
相關標籤/搜索