@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")
然后记得文件输入输出就可以了。