[BZOJ 2555] SubString

Substring

题意

题面

给定一个初始字符串, 要求支持在这个字符串后添加字符串/查询某个字符串作为子串的出现次数.

强制在线.

长度 \(\le600000\),询问次数 \(\le10000\),询问总长度 \(\le3000000\).

题解

看上去好像可以KMP增量构造的样子

实际上不行 (每次询问都得遍历一遍原串)

考虑用另一个可以支持快速扩展的维护字符串的算法: SAM.

但是SAM增量构造的时候某个状态对应的 right 集合大小(也就是对应子串的出现次数)是 prt 树上的子树和的形式. 而且这个树的形态会产生改变(又是烦人的nq结点搞的事情).

注意到这里求子树和的时候不会换根, 于是我们可以在每次 link 的时候都把当前点对所有祖先的贡献都通过一次链上操作累加上去. 于是就可以比较容易地用LCT维护了.

然而写假了好几次

一开始写假是因为 Decode 出了锅调了很久...因为我一直以为 maskDecode 的时候也会变...

后来写假是因为 link/cut 的时候没有把整个子树的贡献都累加上去 (这数据比较生草...这么大的错误只挂了一个点...)

参考代码

#include <bits/stdc++.h>

const int MAXN=2e6+10;
int size[MAXN];

#define lch chd[0]
#define rch chd[1]
#define kch chd[k]
#define xch chd[k^1]

struct LinkCutTree{
    struct Node{
        int val;
        int add;
        bool rev;
        Node* prt;
        Node* pprt;
        Node* chd[2];
        Node(int x):val(x),add(0),rev(false),prt(NULL),pprt(NULL){
            this->lch=this->rch=NULL;
        }
        void Flip(){
            if(this!=NULL){
                this->rev=!this->rev;
                std::swap(this->lch,this->rch);
            }
        }
        void Add(int x){
            if(this!=NULL){
                this->val+=x;
                this->add+=x;
            }
        }
        void PushDown(){
            if(this->add!=0){
                this->lch->Add(this->add);
                this->rch->Add(this->add);
                this->add=0;
            }
            if(this->rev){
                this->lch->Flip();
                this->rch->Flip();
                this->rev=false;
            }
        }
    };
    std::vector<Node*> N;
    LinkCutTree():N(1){}
    void Rotate(Node* root,int k){
        Node* tmp=root->xch;
        root->PushDown();
        tmp->PushDown();
        tmp->prt=root->prt;
        if(root->prt==NULL){
            tmp->pprt=root->pprt;
            root->pprt=NULL;
        }
        else if(root->prt->lch==root)
            root->prt->lch=tmp;
        else
            root->prt->rch=tmp;
        root->xch=tmp->kch;
        if(root->xch!=NULL)
            root->xch->prt=root;
        tmp->kch=root;
        root->prt=tmp;
    }
    void Splay(Node* root){
        while(root->prt!=NULL){
            int k=root->prt->lch==root;
            if(root->prt->prt==NULL)
                Rotate(root->prt,k);
            else{
                int d=root->prt->prt->lch==root->prt;
                Rotate(k==d?root->prt->prt:root->prt,k);
                Rotate(root->prt,d);
            }
        }
    }
    void Expose(Node* root){
        Splay(root);
        root->PushDown();
        if(root->rch!=NULL){
            root->rch->prt=NULL;
            root->rch->pprt=root;
            root->rch=NULL;
        }
    }
    bool Splice(Node* root){
        Splay(root);
        if(root->pprt==NULL)
            return false;
        Expose(root->pprt);
        root->pprt->rch=root;
        root->prt=root->pprt;
        root->pprt=NULL;
        return true;
    }
    void Access(Node* root){
        Expose(root);
        while(Splice(root));
        assert(root->pprt==0&&root->prt==0);
    }
    void Evert(Node* root){
        Access(root);
        root->Flip();
    }
    void Add(int y,int d){
        Evert(N[1]);
        Access(N[y]);
        N[y]->Add(d);
    }
    void Link(int prt,int son){
        Evert(N[son]);
        N[son]->pprt=N[prt];
        Evert(N[1]);
        Access(N[prt]);
        N[prt]->Add(N[son]->val);
    }
    void Cut(int prt,int son){
        Evert(N[1]);
        Access(N[son]);
        Access(N[prt]);
        N[prt]->Add(-N[son]->val);
        Access(N[son]);
        N[son]->PushDown();
        N[son]->lch->prt=NULL;
        N[son]->lch=NULL;
    }
    int Query(int x){
        Access(N[x]);
        return N[x]->val;
    }
    void MakeTree(int x){
        N.push_back(new Node(x));
    }
}*T=new LinkCutTree();

int q;
int cnt=1;
int root=1;
int last=1;
char s[MAXN];
int prt[MAXN];
int len[MAXN];
std::map<char,int> chd[MAXN];

int Query(char*);
void Extend(char);
void Extend(char*);
void Decode(char*,int);

int main(){
    scanf("%d",&q);
    scanf("%s",s);
    T->MakeTree(0);
    Extend(s);
    int mask=0;
    while(q--){
        scanf("%s",s);
        if(*s=='A'){
            scanf("%s",s);
            Decode(s,mask);
            Extend(s);
        }
        else{
            scanf("%s",s);
            Decode(s,mask);
            int x=Query(s);
            mask^=x;
            printf("%d\n",x);
        }
    }
    return 0;
}

int Query(char* s){
    int cur=root;
    while(*s!='\0'){
        if(!chd[cur].count(*s))
            return 0;
        else
            cur=chd[cur][*s];
        ++s;
    }
    return T->Query(cur);
}

void Decode(char* s,int mask){
    int len=strlen(s);
    for(int i=0;i<len;i++){
        mask=(mask*131+i)%len;
        char t=s[i];
        s[i]=s[mask];
        s[mask]=t;
    }
}

void Extend(char* s){
    while(*s!='\0')
        Extend(*(s++));
}

void Extend(char x){
    int p=last;
    int np=++cnt;
    T->MakeTree(1);
    size[last=np]=1;
    len[np]=len[p]+1;
    while(p&&!chd[p].count(x))
        chd[p][x]=np,p=prt[p];
    if(p==0)
        prt[np]=root;
    else{
        int q=chd[p][x];
        if(len[q]==len[p]+1)
            prt[np]=q;
        else{
            int nq=++cnt;
            T->MakeTree(0);
            chd[nq]=chd[q];
            prt[nq]=prt[q];
            T->Cut(prt[q],q);
            T->Link(prt[nq],nq);
            prt[q]=nq;
            T->Link(nq,q);
            prt[np]=nq;
            len[nq]=len[p]+1;
            while(p&&chd[p][x]==q)
                chd[p][x]=nq,p=prt[p];
        }
    }
    T->Link(prt[np],np);
}

猜你喜欢

转载自www.cnblogs.com/rvalue/p/10359563.html