• @bzoj



    @description@

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

    input
    第一行一个数Q表示操作个数。
    第二行一个字符串表示初始字符串init。
    接下来Q行,每行2个字符串Type,Str。
    Type是ADD的话表示在后面插入字符串。
    Type是QUERY的话表示询问某字符串在当前字符串中出现了几次。
    输入是加密的。具体怎么加密的大家还是回去看原题面吧。

    output
    对于每次询问,输出相应的答案。

    sample input
    2
    A
    QUERY B
    ADD BBABBBBAAB
    sample output
    0

    @solution@

    假如只询问,那么就是建完后缀自动机然后将 parent 树中子树里的有效结点(即每次增加新字符才产生的结点)个数统计出来。

    那么现在加上了修改,涉及到加边删边,然后求解子树信息。
    恩。写 LCT 吧。(其实还可以平衡树维护 dfs 序不过想到我 LCT 几百年没有写过来练练手)

    LCT 维护子树信息的时候需要两个信息:虚边连着的儿子的信息和总信息。总信息可以通过虚边信息 + splay 中信息 + 结点信息就可以维护出来了。
    然后在 access 和 link/cut 这几个涉及到虚边儿子的变动的操作维护一下虚边信息就可以了。

    本题树是有向的,所以不能随便就换根什么的。

    另外,splay 中维护的信息是整棵 splay 树的信息,而这棵 splay 树还包含结点的祖先(即结点的左子树),记得减去这部分的贡献。

    只要你有耐心,一定可以过这道题的。

    @accepted code@

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 3000000;
    const int MAXM = 600000;
    struct LCT{
        struct node{
            node *fa, *ch[2]; int siz, key, ans, vir;
        }pl[MAXM*2 + 5], *ncnt, *NIL;
        bool is_root(node *x) {
        	return x->fa->ch[0] != x && x->fa->ch[1] != x;
        }
        void set_child(node *x, node *y, int d) {
        	if( y != NIL ) y->fa = x;
        	if( x != NIL ) x->ch[d] = y;
        }
        void init() {
            ncnt = NIL = &pl[0];
            ncnt->fa = ncnt->ch[0] = ncnt->ch[1] = NIL;
        }
        node *newnode(int x) {
    		ncnt++; ncnt->key = ncnt->siz = ncnt->ans = x; ncnt->vir = 0;
    		ncnt->fa = ncnt->ch[0] = ncnt->ch[1] = NIL;
    		return ncnt;
        }
        void pushup(node *x) {
        	x->ans = x->vir + x->ch[0]->ans + x->ch[1]->ans + x->key;
        }
        void rotate(node *x) {
    		node *y = x->fa; int d = (y->ch[1] == x);
    		if( is_root(y) ) x->fa = y->fa;
    		else set_child(y->fa, x, y->fa->ch[1] == y);
    		set_child(y, x->ch[!d], d);
    		set_child(x, y, !d);
    		pushup(y);
        }
        void splay(node *x) {2555: SubString
    		node *y;
    		while( !is_root(x) ) {
    			y = x->fa;
    			if( is_root(y) )
    				rotate(x);
    			else {
    				if( (y->fa->ch[1] == y) == (y->ch[1] == x) )
    					rotate(y);
    				else rotate(x);
    				rotate(x);
    			}
    		}
    		pushup(x);
        }
        void access(node *x) {
        	node *y = NIL;
    		while( x != NIL ) {
    			splay(x);
    			x->vir += x->ch[1]->ans, x->vir -= y->ans;
    			x->ch[1] = y, pushup(x);
    			y = x, x = x->fa;
    		}
        }
        void cut(node *x, node *y) {
        	access(x), splay(x);
        	access(y), splay(y);
    		y->vir -= x->ans;
    		pushup(y); x->fa = NIL;
        }
        void link(node *x, node *y) {
        	access(x), splay(x);
        	access(y), splay(y);
    		x->fa = y; y->vir += x->ans;
    		pushup(y);
        }
        void debug() {
        	for(int i=1;i<=ncnt-pl;i++)
        		printf("%d : %d %d %d	%d %d
    ", i, pl[i].ch[0]-pl, pl[i].ch[1]-pl, pl[i].fa-pl, pl[i].vir, pl[i].ans);
        }
    };
    struct SAM{
        struct node{
            node *ch[26], *fa; int mx;
            LCT::node *ad;
        }pl[MAXM*2 + 5], *root, *lst, *ncnt;
        LCT lct;
        void init() {
        	lct.init();
            root = lst = ncnt = &pl[0];
            for(int i=0;i<26;i++)
                root->ch[i] = NULL;
            root->fa = NULL, root->mx = 0, root->ad = lct.newnode(0);
        }
        node *newnode(int x) {
            ncnt++;
            for(int i=0;i<26;i++)
                ncnt->ch[i] = NULL;
            ncnt->fa = NULL, ncnt->mx = 0, ncnt->ad = lct.newnode(x);
            return ncnt;
        }
        void link(node *a, node *b) {
            if( a->fa ) lct.cut(a->ad, a->fa->ad);
            //printf("%d %d
    ", a->ad-lct.pl, b->ad-lct.pl);
            a->fa = b; lct.link(a->ad, b->ad);
        }
        void extend(int x) {
            node *cur = newnode(1), *p = lst;
            cur->mx = lst->mx + 1, lst = cur;
            while( p && !p->ch[x] )
                p->ch[x] = cur, p = p->fa;
            if( !p )
                link(cur, root);
            else {
                node *q = p->ch[x];
                if( q->mx == p->mx + 1 )
                    link(cur, q);
                else {
                    node *cne = newnode(0);
                    for(int i=0;i<26;i++)
                    	cne->ch[i] = q->ch[i];
                   	link(cne, q->fa); cne->mx = p->mx + 1;
                    link(q, cne), link(cur, cne);
                    while( p && p->ch[x] == q )
                        p->ch[x] = cne, p = p->fa;
                }
            }
        }
    }sam;
    int mask = 0;
    void decode(char *s, int m) {
        int lens = strlen(s);
        for(int i=0;i<lens;i++) {
            m = (m * 131 + i) % lens;
            swap(s[i], s[m]);
        }
    }
    void query(char *s) {
        int lens = strlen(s);
        SAM::node *nw = sam.root;
        for(int i=0;i<lens;i++) {
            if( !nw->ch[s[i] - 'A'] ) {
                printf("%d
    ", 0);
                return ;
            }
            nw = nw->ch[s[i] - 'A'];
        }
    	sam.lct.access(nw->ad); sam.lct.splay(nw->ad);
        //sam.lct.debug();
        printf("%d
    ", nw->ad->ans - nw->ad->ch[0]->ans);
        mask ^= nw->ad->ans - nw->ad->ch[0]->ans;
    }
    void insert(char *s) {
        int lens = strlen(s);
        for(int i=0;i<lens;i++)
            sam.extend(s[i] - 'A');
    }
    char s[MAXN + 5], op[10];
    int main() {
        sam.init();
        int Q; scanf("%d", &Q);
        scanf("%s", s), insert(s);
        for(int i=1;i<=Q;i++) {
            scanf("%s", op);
            if( op[0] == 'Q' )
                scanf("%s", s), decode(s, mask), query(s);
            else scanf("%s", s), decode(s, mask), insert(s);
        }
    }
    

    @details@

    MMP 我……我无话可说了。
    我居然把后缀自动机中的 cne->mx = p->mx + 1 写成了 cne->mx = q->mx + 1
    ……
    然后我就整整调试了一天。

    乐观地想,我通过这道题学会了 ubuntu 怎么对拍不是吗。
    其实也比较简单。你只需要 system(".1.exe") 然后记得文件输入输出就可以了。

  • 相关阅读:
    茶香账簿小程序开发进度(3)
    软件需求十步走阅读笔记(一)
    茶香账簿小程序开发进度(2)
    茶香账簿小程序开发进度(1)
    软件工程概论总结及对老师的意见
    人月神话阅读笔记03
    第二阶段第七次站立会议
    第二阶段第六次站立会议
    第二阶段第五次站立会议
    第二阶段第四次站立会议
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10256107.html
Copyright © 2020-2023  润新知