zoukankan      html  css  js  c++  java
  • [BZOJ2555] SubString

    Description

    给定一个初始字符串,要求支持两种操作:在当前字符串后面加上一个字符串;询问一个字符串在当前字符串中作为子串出现了几次。

    Solution

    LCT 维护后缀自动机,询问的就是后缀树的子树大小

    于是我们暴力维护子树大小


    调了大半天,最后终于用一个非常 tricky 的办法把它搞定了

    首先这是因为我之前写的维护子树和都是假的(实际上只对整个连通块保持正确性,毕竟是从 BJOI2014 大融合 那题引申出来的

    将计就计,我们干脆在每次询问的时候把对应子树单独切出来求和,然后再连接回去

    #include <bits/stdc++.h>
    using namespace std;
    const int Maxn = 3000005;
    const int N = 3000005;
    
    string decodeWithMask(string s, int mask) {
    	for (int j = 0; j < s.length(); j++) {
    		mask = (mask * 131 + j) % s.length();
    		char t = s[j];
    		s[j] = s[mask];
    		s[mask] = t;
    	}
    	return s;
    }
    
    namespace lct{
    	int top, q[N], ch[N][2], fa[N], rev[N], val[N];
    	int sz[N], si[N];
    	inline void pushup(int x){
    		sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+si[x]+val[x]; //
    	}
    	inline void pushdown(int x){
    		if(!rev[x]) return;
    		rev[ch[x][0]]^=1;
    		rev[ch[x][1]]^=1;
    		rev[x]^=1;
    		swap(ch[x][0],ch[x][1]);
    	}
    	inline bool isroot(int x){
    		return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
    	}
    	inline void rotate(int p){
    		int q=fa[p], y=fa[q], x=ch[fa[p]][1]==p;
    		ch[q][x]=ch[p][x^1]; fa[ch[q][x]]=q;
    		ch[p][x^1]=q; fa[q]=p; fa[p]=y;
    		if(y) if(ch[y][0]==q) ch[y][0]=p;
    		else  if(ch[y][1]==q) ch[y][1]=p;
    		pushup(q); pushup(p);
    	}
    	inline void splay(int x){
    		q[top=1]=x;
    		for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
    		for(int i=top;i;i--) pushdown(q[i]);
    		for(;!isroot(x);rotate(x))
    			if(!isroot(fa[x]))
    				rotate((ch[fa[x]][0]==x)==(ch[fa[fa[x]]][0]==fa[x])?fa[x]:x);
    	}
    	void access(int x){
    		for(int t=0;x;t=x,x=fa[x])
    			splay(x),si[x]+=sz[ch[x][1]],si[x]-=sz[t],ch[x][1]=t,pushup(x); //
    	}
    	void makeroot(int x){
    		access(x);
    		splay(x);
    		rev[x]^=1;
    	}
    	int find(int x){
    		access(x);
    		splay(x);
    		while(ch[x][0]) x=ch[x][0];
    		return x;
    	}
    	void split(int x,int y){
    		makeroot(x);
    		access(y);
    		splay(y);
    	}
    	void cut(int x,int y){
    	    if(!x || !y) return;
    		split(x,y);
    		if(ch[y][0]==x)
    			ch[y][0]=0, fa[x]=0;
            pushup(y); //
            makeroot(1);
    	}
    	void link(int x,int y){
    	    access(y);
    		splay(y);
    		fa[x]=y;
    		si[y]+=sz[x]; //
    		pushup(y); //
    		makeroot(1);
    	}
    	void print()
    	{
    	    for(int i=1;i<=10;i++)
            {
                cout<<" vertex "<<i<<"  sz="<<sz[i]<<"  si="<<si[i]<<"  fa="<<fa[i]<<"  ch="<<ch[i][0]<<","<<ch[i][1]<<endl;
            }
    	}
    }
    
    
    
    namespace sam {
        int maxlen[Maxn], trans[Maxn][26], link[Maxn], Size=1, Last=1;
        int t[Maxn], a[Maxn], cnt[Maxn], f[Maxn];
        inline void extend(int id) {
            int cur = (++ Size), p;
            lct::val[Size]=1;
            lct::pushup(Size);
            maxlen[cur] = maxlen[Last] + 1;
            cnt[cur] = 1;
            for (p = Last; p && !trans[p][id]; p = link[p]) trans[p][id] = cur;
            if (!p) link[cur] = 1, lct::link(cur,1);
            else {
                int q = trans[p][id];
                if (maxlen[q] == maxlen[p] + 1) link[cur] = q, lct::link(cur,q);
                else {
                    int clone = (++ Size);
                    //lct::sz[Size]=1;
                    maxlen[clone] = maxlen[p] + 1;
                    for(int i=0;i<26;i++) trans[clone][i] = trans[q][i];
                    link[clone] = link[q];
                    lct::link(clone,link[q]);
                    for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
    
                    if(link[q]) lct::cut(q,link[q]);
                    link[cur] = link[q] = clone;
    
                    lct::link(cur,clone);
                    lct::link(q,clone);
                }
            }
            Last = cur;
        }
        void extend(string s)
        {
            for(char t:s)
            {
                extend(t-'A');
                lct::access(1);
                lct::splay(1);
                //lct::print();
            }
        }
        int query(string s)
        {
            int p=1,len=s.length();
            for(int i=0;i<len;i++)
            {
                p=trans[p][s[i]-'A'];
                if(!p) return 0;
            }
            //cout<<"query "<<p<<endl;
            if(link[p]) lct::cut(p,link[p]);
            lct::makeroot(p);
            int ans = lct::sz[p];
            if(link[p]) lct::link(p,link[p]);
            lct::makeroot(1);
            return ans;
        }
    }
    
    signed main()
    {
        int n,lastans=0;
        cin>>n;
        string s;
        cin>>s;
        sam::extend(s);
        string op;
        while(n--)
        {
            cin>>op>>s;
            if(op[0]=='A')
            {
                sam::extend(decodeWithMask(s,lastans));
            }
            else
            {
                int tans;
                cout<<(tans=sam::query(decodeWithMask(s,lastans)))<<endl;
                lastans^=tans;
            }
            //lct::print();
        }
    }
    
    /*
    20
    BBBAABABBA
    ADD ABBABABABBBB
    QUERY BABABAABBA
    QUERY AABABBBAAB
    QUERY BABABBABAABBBB
    ADD BABAAAAAABABB
    ADD BABABBBBABBAAA
    ADD BBBAABABAA
    QUERY BBBBAAA
    ADD BBBAAABBBA
    QUERY BABBBBA
    QUERY BABABBAAB
    ADD BBBBBBBBB
    ADD BAABAAB
    ADD BAAAAAB
    QUERY ABBBBBB
    ADD ABBBAAA
    QUERY ABABBBAABBABA
    QUERY ABAAAABBABBB
    ADD AAAAABAAAAA
    QUERY ABBAABBBBBB
    */
    
    
  • 相关阅读:
    Java 多个线程之间共享数据
    Mysql索引为什么要采用B+Tree而非B-Tree
    MyBatis常见面试题:通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
    CentOS 8.1 基于二进制安装docker
    shell实现一键证书申请和颁发脚本
    配置DNS的主从以及实现域名反向解析
    利用Dockerfile实现nginx的部署
    编译安装Mariadb-10.5.5
    登录mysql出错:mysql: error while loading shared libraries: libtinfo.so.5: cannot open share
    一键安装MySQL5.7脚本
  • 原文地址:https://www.cnblogs.com/mollnn/p/13274446.html
Copyright © 2011-2022 走看看