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


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

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

    数据范围:$n≤100000,d≤a_i<m≤10^9,q≤5 imes 10^5$

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

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

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

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

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

     

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

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

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

    就像这样

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

    我们只需要不断地做搬动节点的操作(在平衡树中,取出一个区间,并把这个区间插入到另一个区间中),并且动态维护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
    ",ans[i]);
    172 }
  • 相关阅读:
    Linux常见问题解决
    使用npm国内镜像
    常用CSS备忘
    如何把JavaScript数组中指定的一个元素移动到第一位
    教你如何将word中的表格完美粘贴到ppt中
    测试开发之路--一个小小工程师的回首
    一篇文章读完50篇摄影教程(托马斯的2016总结)
    李开复推荐的30本创业/管理/互联网必须看的电子书
    摩拜单车深度产品体验报告
    Word2016(2013)怎么从任意页插入起始页码
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/11017030.html
Copyright © 2020-2023  润新知