• bzoj 2555: SubString 后缀自动机+LCT


    题目:

    给你一个字符串init,要求你支持两个操作

    • 在当前字符串的后面插入一个字符串
    • 询问字符串s在当前字符串中出现了几次?(作为连续子串)
      你必须在线支持这些操作。

    题解:

    询问某个字符串出现了几次,我们可以直接在后缀自动机上跑
    输出最终走到的状态的right集合大小
    如果失配则没有出现过

    不难发现这样是正确的
    所以现在问题在于求right集合的大小.由于这道题强制在线
    所以我们需要动态维护right集合的大小
    就是说每次加入一个cur后,把cur的parent树的所有祖先的siz全部+1
    如果我们直接跳parent树会TLE.不要问我是怎么知道的
    想为什么会TLE
    如果是这样的字符串

    input
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...(省略若干a)

    这样每一次修改所有的祖先的时候复杂度都是(O(n))的,也就是整体复杂度退化到了(O(n^2))

    所以我们需要用LCT来维护parent树
    这样就可以做到(O(nlogn))

    一直在刷后缀自动机
    为了杠这道题还特意花了一个下午去学LCT

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const int maxn = 600010;
    const int maxm = 3000010;
    namespace lct{
    	struct Node{
    		Node *ch[2],*fa;
    		int w,lazy;
    	}*null;
    	Node mem[maxn<<1],*it;
    	inline void init(){
    		it = mem;null = new Node;
    		null->ch[0] = null->ch[1] = null->fa = null;
    		null->w = null->lazy = 0;
    	}
    	inline Node* newNode(int val){
    		Node *p = it++;p->w = val;p->lazy = 0;
    		p->ch[0] = p->ch[1] = p->fa = null;
    		return p;
    	}
    	inline void pushdown(Node *p){
    		if(p->lazy == 0 || p == null) return;
    		if(p->ch[0] != null){
    			p->ch[0]->w += p->lazy;
    			p->ch[0]->lazy += p->lazy;
    		}
    		if(p->ch[1] != null){
    			p->ch[1]->w += p->lazy;
    			p->ch[1]->lazy += p->lazy;
    		}
    		p->lazy = 0;
    	}
    	inline void rotate(Node *p,Node *x){
    		int k = p == x->ch[1];
    		Node *y = p->ch[k^1],*z = x->fa;
    		if(z->ch[0] == x) z->ch[0] = p;
    		if(z->ch[1] == x) z->ch[1] = p;
    		if(y != null) y->fa = x;
    		p->fa = z;p->ch[k^1] = x;
    		x->fa = p;x->ch[k] = y;
    	}
    	inline bool isroot(Node *p){
    		return p == null || (p->fa->ch[0] != p && p->fa->ch[1] != p);
    	}
    	inline void splay(Node *p){
    		pushdown(p);
    		while(!isroot(p)){
    			Node *x = p->fa,*y = x->fa;
    			pushdown(y);pushdown(x);pushdown(p);
    			if(isroot(x)) rotate(p,x);
    			else if((x->ch[0] == p) ^ (y->ch[0] == x)) rotate(p,x),rotate(p,y);
    			else rotate(x,y),rotate(p,x);
    		}
    	}
    	inline void Access(Node *x){
    		Node *y = null;
    		while(x != null){
    			splay(x);x->ch[1] = y;
    			y = x;x = x->fa;
    		}
    	}
    	inline void link(Node *u,Node *v){
    		Access(u);splay(u);
    		u->fa = v;
    	}
    	inline void cut(Node *u){
    		Access(u);splay(u);
    		u->ch[0] = u->ch[0]->fa = null;
    	}
    	inline int query(Node *p){
    		Access(p);splay(p);
    		return p->w;
    	}
    	inline void inc(Node* x){
    		Access(x);splay(x);
    		x->lazy ++ ;x->w ++ ;
    	}
    }
    struct Node{
    	int nx[26];
    	int len,fa;
    }T[maxn<<1];
    int last,nodecnt;
    inline void init(){
    	last = nodecnt = 0;
    	T[0].fa = -1;
    }
    inline void insert(char cha){
    	int c = cha - 'A',cur = ++ nodecnt,p;lct::newNode(0);
    	T[cur].len = T[last].len + 1;
    	for(p = last;p != -1 && !T[p].nx[c];p = T[p].fa) T[p].nx[c] = cur;
    	if(p == -1){
    		T[cur].fa = 0;
    		lct::link(lct::mem+cur,lct::mem);
    	}else{
    		int q = T[p].nx[c];
    		if(T[q].len == T[p].len + 1){
    			T[cur].fa = q;
    			lct::link(lct::mem+cur,lct::mem+q);
    		}else{
    			int co = ++ nodecnt;lct::newNode(query((lct::mem+q)));
    			T[co] = T[q];T[co].len = T[p].len+1;
    			lct::link(lct::mem+co,lct::mem+T[q].fa);
    			for(;p != -1 && T[p].nx[c] == q;p = T[p].fa) T[p].nx[c] = co;
    			lct::cut(lct::mem+q);
    			lct::link(lct::mem+cur,lct::mem+co);
    			lct::link(lct::mem+q,lct::mem+co);
    			T[cur].fa = T[q].fa = co;
    		}
    	}last = cur;
    	lct::inc(lct::mem+cur);
    }
    char cmd[10];
    char s[maxm];
    void decode(int mask){
    	int len = strlen(s);
    	for(int i=0;i<len;++i){
    		mask = (mask*131 + i) % len;
    		swap(s[i],s[mask]);
    	}
    }
    int mask = 0;
    inline int find(){
    	int len = strlen(s),nw = 0;
    	for(int i=0;i<len;++i){
    		if(T[nw].nx[s[i]-'A']) nw = T[nw].nx[s[i]-'A'];
    		else return 0;
    	}
    	int x = lct::query(lct::mem+nw);
    	mask ^= x;return x;
    }
    int main(){
    	init();lct::init();lct::newNode(0);
    	int m;read(m);scanf("%s",s);
    	int len = strlen(s);for(int i=0;i<len;++i) insert(s[i]);
    	while(m--){
    		scanf("%s",cmd);scanf("%s",s);
    		decode(mask);
    		if(*cmd == 'A'){
    			int len = strlen(s);
    			for(int i=0;i<len;++i) insert(s[i]);
    		}else printf("%d
    ",find());
    	}
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    【BZOJ2734】【HNOI2012】集合选数(状态压缩,动态规划)
    【Luogu1879】玉米田(状态压缩,动态规划)
    【BZOJ1911】【APIO2010】特别行动队(斜率优化,动态规划)
    蒟蒻关于斜率优化DP简单的总结
    【BZOJ1010】【HNOI2008】玩具装箱(斜率优化,动态规划)
    【BZOJ4196】【NOI2015】软件包管理器(树链剖分,线段树)
    【BZOJ1483】【HNOI2009】梦幻布丁(启发式合并,平衡树)
    【BZOJ1058】【ZJOI2007】报表统计(链表,堆,Splay)
    【BZOJ1012】【JSOI2008】最大数(线段树)
    【SHOI2012】魔法树(树链剖分,线段树)
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6523864.html
Copyright © 2020-2023  润新知