[BZOJ 2555] SubString
生活随笔
收集整理的這篇文章主要介紹了
[BZOJ 2555] SubString
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Substring
題意
題面
給定一個初始字符串, 要求支持在這個字符串后添加字符串/查詢某個字符串作為子串的出現次數.
強制在線.
長度 \(\le600000\),詢問次數 \(\le10000\),詢問總長度 \(\le3000000\).
題解
看上去好像可以KMP增量構造的樣子
實際上不行 (每次詢問都得遍歷一遍原串)
考慮用另一個可以支持快速擴展的維護字符串的算法: SAM.
但是SAM增量構造的時候某個狀態對應的 right 集合大小(也就是對應子串的出現次數)是 prt 樹上的子樹和的形式. 而且這個樹的形態會產生改變(又是煩人的nq結點搞的事情).
注意到這里求子樹和的時候不會換根, 于是我們可以在每次 link 的時候都把當前點對所有祖先的貢獻都通過一次鏈上操作累加上去. 于是就可以比較容易地用LCT維護了.
然而寫假了好幾次
一開始寫假是因為 Decode 出了鍋調了很久...因為我一直以為 mask 在 Decode 的時候也會變...
后來寫假是因為 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;elseroot->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;elsecur=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); }轉載于:https://www.cnblogs.com/rvalue/p/10359563.html
總結
以上是生活随笔為你收集整理的[BZOJ 2555] SubString的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读《程序是怎样跑起来的》第七章有感
- 下一篇: 作用域,上下文,闭包