zoukankan      html  css  js  c++  java
  • bzoj2555 SubString

    传送门

    **什么破题……

    看题的时候突然发现:咦这题我怎么见过……后来想起来是sxysxy说给我的一道后缀自动机+LCT的破题,然后我偏不信这个邪,码了个分块hash试图水过去,然后发现不是TLE就是MLE……没办法,只能乖乖写正解了……

    加入操作只会在原串末尾加一个字符串,不难想到维护当前串的后缀自动机,加入的时候直接逐个加入字符即可。询问的时候首先在后缀自动机上进行转移,转移到的那个点的right大小即为答案。而一个点的right大小就等于parent树上子树中不是因复制而产生的节点个数(似乎表达有点问题,凑合看吧……),而parent树的形态会发生变化,因此可以用LCT维护子树大小,实现的时候把这个点的父亲到根的值全部加上/减去这个子树的大小即可。

    后缀自动机的更新均摊O(1),LCT单次操作均摊O(logn),总复杂度就是$O(|S|log |S|+sum{|T|}+Qlog |S|)$,反正跑得挺快就是了。

      1 /**************************************************************
      2     Problem: 2555
      3     User: hzoier
      4     Language: C++
      5     Result: Accepted
      6     Time:9748 ms
      7     Memory:47112 kb
      8 ****************************************************************/
      9 #include<cstdio>
     10 #include<cstring>
     11 #include<algorithm>
     12 #define isroot(x) ((x)->p==null||((x)->p->ch[0]!=(x)&&((x)->p->ch[1]!=(x))))
     13 #define dir(x) ((x)==(x)->p->ch[1])
     14 using namespace std;
     15 const int maxn=1200010,maxm=3000010;
     16 struct node{
     17     int key,lazy;
     18     node *ch[2],*p;
     19     node(int d=0):key(d),lazy(0){}
     20     inline void pushdown(){
     21         if(!lazy)return;
     22         key+=lazy;
     23         ch[0]->lazy+=lazy;
     24         ch[1]->lazy+=lazy;
     25         lazy=0;
     26     }
     27 }null[maxn];
     28 void decode(char*,int,int);
     29 void expand(int);
     30 int travel(const char*);
     31 node *access(node*);
     32 void link(node*,node*);
     33 void cut(node*);
     34 void splay(node*);
     35 void rot(node*,int);
     36 int root,last,cnt=0,val[maxn]={0},par[maxn]={0},go[maxn][2]={{0}};
     37 char s[maxm],c[maxn];
     38 int n,q,mask=0,ans;
     39 int main(){
     40     null->ch[0]=null->ch[1]=null->p=null;
     41     root=last=++cnt;
     42     null[root].ch[0]=null[root].ch[1]=null[root].p=null;
     43     null[root].key=1;
     44     scanf("%d%s",&q,s);
     45     n=strlen(s);
     46     for(int i=0;i<n;i++)expand(s[i]-'A');
     47     while(q--){
     48         scanf("%s%s",c,s);
     49         decode(s,n=strlen(s),mask);
     50         if(!strcmp(c,"ADD"))for(int i=0;i<n;i++)expand(s[i]-'A');
     51         else{
     52             printf("%d
    ",ans=travel(s));
     53             mask^=ans;
     54         }
     55     }
     56     return 0;
     57 }
     58 void decode(char *s,int n,int mask){
     59     for(int i=0;i<n;i++){
     60         mask=(mask*131+i)%n;
     61         swap(s[i],s[mask]);
     62     }
     63 }
     64 void expand(int c){
     65     int p=last,np=++cnt;
     66     val[np]=val[p]+1;
     67     null[np].ch[0]=null[np].ch[1]=null[np].p=null;
     68     null[np].key=1;
     69     while(p&&!go[p][c]){
     70         go[p][c]=np;
     71         p=par[p];
     72     }
     73     if(!p){
     74         par[np]=root;
     75         link(null+np,null+root);
     76     }
     77     else{
     78         int q=go[p][c];
     79         if(val[q]==val[p]+1){
     80             par[np]=q;
     81             link(null+np,null+q);
     82         }
     83         else{
     84             int nq=++cnt;
     85             val[nq]=val[p]+1;
     86             memcpy(go[nq],go[q],sizeof(go[q]));
     87             null[nq].ch[0]=null[nq].ch[1]=null[nq].p=null;
     88             par[nq]=par[q];
     89             link(null+nq,null+par[q]);
     90             par[np]=par[q]=nq;
     91             link(null+np,null+nq);
     92             cut(null+q);
     93             link(null+q,null+nq);
     94             while(p&&go[p][c]==q){
     95                 go[p][c]=nq;
     96                 p=par[p];
     97             }
     98         }
     99     }
    100     last=np;
    101 }
    102 int travel(const char *c){
    103     int x=root;
    104     while(*c)x=go[x][*c++-'A'];
    105     if(!x)return 0;
    106     splay(null+x);
    107     return null[x].key;
    108 }
    109 node *access(node *x){
    110     node *y=null;
    111     while(x!=null){
    112         splay(x);
    113         x->ch[1]=y;
    114         y=x;
    115         x=x->p;
    116     }
    117     return y;
    118 }
    119 void link(node *x,node *y){
    120     splay(x);
    121     x->p=y;
    122     access(y)->lazy+=x->key;
    123 }
    124 void cut(node *x){
    125     access(x);
    126     splay(x);
    127     x->ch[0]->lazy-=x->key;
    128     x->ch[0]->p=null;
    129     x->ch[0]=null;
    130 }
    131 void splay(node *x){
    132     x->pushdown();
    133     while(!isroot(x)){
    134         if(!isroot(x->p))x->p->p->pushdown();
    135         x->p->pushdown();
    136         x->pushdown();
    137         if(isroot(x->p)){
    138             rot(x->p,dir(x)^1);
    139             break;
    140         }
    141         if(dir(x)==dir(x->p))rot(x->p->p,dir(x->p)^1);
    142         else rot(x->p,dir(x)^1);
    143         rot(x->p,dir(x)^1);
    144     }
    145 }
    146 void rot(node *x,int d){
    147     node *y=x->ch[d^1];
    148     if((x->ch[d^1]=y->ch[d])!=null)y->ch[d]->p=x;
    149     y->p=x->p;
    150     if(!isroot(x))x->p->ch[dir(x)]=y;
    151     (y->ch[d]=x)->p=y;
    152 }
    153 /*
    154 操作都是向后添加字符,因此考虑后缀自动机。
    155 又因为需要动态维护子树大小,因此用LCT维护。
    156 */
    View Code

    其实还有平衡树维护dfs序列的做法,然而以前没写过没敢写……

    还有两个个脑洞级别的做法(虽然分块hash不知为何过不去……):

    分块hash,设定一个阈值M,把原串所有长度<=M的子串的hash值扔到hash表里,询问时对于长度>M的串直接暴力KMP或者扫一遍原串的hash值,长度<=M的串扔到hash表里询问一下即可,取$M=sqrt{sum{|T|}}$可以达到理论最优复杂度$O(|S|sqrt{sum{|T|}} +sum{|T|}+Q)$,然而用自然溢出hash会被卡,对998244353取模的话就会TLE/MLE……卡我分块,出题人**

    既然可以后缀自动机+LCT,总不能不给后缀数组留条活路吧,所以后缀平衡树出场了。把原串反过来,每次添加一个字符就是在反串的前面添加一个字符,用二分+hash或者别的什么办法就可以快速比较两个反串后缀的大小,询问的时候在后缀平衡树上随便搞搞就行了,复杂度应该是$O(log^2|S|)$,因此如果用二分hash比较后缀大小的话总复杂度应该是$O(|S|log^2|S|+sum{|T|}+Qlog^2|S|)$,看上去不错,然而我没写过后缀平衡树,这个做法也就只能留作脑洞了……

    调了半天的原因居然又是把splay写错了……这人没救了

  • 相关阅读:
    通过ida dump Uinity3D的加密dll
    apk文件签名绕过
    防止apk反编译的技术分析浅谈--内存修改器篇
    OD脚本指令集
    RHEL6.4 xclock安装小记
    PyCharm配置SFTP远程调试Django应用
    git常用命令
    centos7下yum安装mysql
    慢吞吞的pip切换源
    Linux常用软件安装与配置——目录
  • 原文地址:https://www.cnblogs.com/hzoier/p/6395317.html
Copyright © 2011-2022 走看看