【NOI2019集训题2】 序列 后缀树+splay+dfs序

题目大意:给你一个长度为$n$的序列$a_i$,还有一个数字$m$,有$q$次询问

每次给出一个$d$和$k$,问你对所有的$a_i$都在模$m$意义下加了$d$后,第$k$小的后缀的起点编号。

数据范围:$n≤100000,d≤a_i

 

这一题我想的时候被最后一步卡主了(其实如果到那个时候估计也时间不够了)

我们不难找出一个单次询问$O(n)$的方法,我们每次暴力更新$a_i$,然后对原序列搞一棵后缀树出来,在上面暴力查询第$k$小即可。

如果没有加$d$的操作,只是单纯询问第k大的话,我们考虑对这棵后缀树按字典序先序遍历一遍,搞出dfs序,用平衡树维护这些点出现的先后顺序。

对于一次询问第$k$小,我们在平衡树上找出dfs序第$k$小的后缀节点出来,输出即可。

下面考虑加$d$的操作。

 

考虑到这一题并没有要求强制在线,我们考虑对所有的询问按$d$从小大到大排序。

随着d的增长,原先最大的数随着取模操作的发生,会变成最小的数。

也就是说会出现一个换位的情况

【NOI2019集训题2】 序列 后缀树+splay+dfs序_第1张图片

 

就像这样

显然这个红点下面还会接很多个节点,但是不管怎么换位,以红点为根的子树内孩子的数量是不会变的。

我们只需要不断地做搬动节点的操作(在平衡树中,取出一个区间,并把这个区间插入到另一个区间中),并且动态维护dfs序列即可。

在询问离线后,我们不难发现每个节点至多只需要被搬动一次。

那么时间复杂度就变成愉快的O((n+q)\ log\ n)了。

 

然而我在想的时候,并没有往dfs序上想,sam也不熟练

注意细节!

  1 #include
  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){
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 }

转载于:https://www.cnblogs.com/xiefengze1/p/11017030.html

你可能感兴趣的:(【NOI2019集训题2】 序列 后缀树+splay+dfs序)