zoukankan      html  css  js  c++  java
  • [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
    ",x);
    		}
    	}
    	return 0;
    }
    
    int Query(char* s){
    	int cur=root;
    	while(*s!=''){
    		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!='')
    		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);
    }
    

  • 相关阅读:
    [反汇编练习] 160个CrackMe之002
    [反汇编练习]160个CrackMe之001
    Leap Motion 开发笔记
    HTMLayout界面CSSS样式解析笔记
    DbgPrint输出格式 Unicodestring
    WinDbg调试命令汇总
    WDF模型驱动程序开发
    [Sciter系列] MFC下的Sciter–5.Sciter中GUI线程研究
    剑指offer第二版-4.二维数组中的查找
    剑指offer第二版-3.数组中重复的数
  • 原文地址:https://www.cnblogs.com/rvalue/p/10359563.html
Copyright © 2011-2022 走看看