@bzoj - 2555@ SubString


@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\t%d %d\n", 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\n", 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\n", 0);
            return ;
        }
        nw = nw->ch[s[i] - 'A'];
    }
	sam.lct.access(nw->ad); sam.lct.splay(nw->ad);
    //sam.lct.debug();
    printf("%d\n", 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") 然后记得文件输入输出就可以了。

posted @ 2019-01-11 17:02  Tiw_Air_OAO  阅读(158)  评论(0编辑  收藏  举报