BZOJ 4545 DQS的Trie

BZOJ 4545 DQS的Trie

第一眼,这不是很裸嘛?

直接构造广义SAM然后跑就行了啊。

动态询问 endpos 集合大小,LCT就好了嘛

(码…)

码了一半,然后发现,每次新加入一个子树啊,复杂度是假的啊?

那这么说网上很多题解貌似都是假的。。。

(你按照dfs的顺序来加,不是就必然会被卡了嘛)

但是这个题不强制在线!

可以先把整个树搭建好再处理询问。

考虑我们加入一个字符,其实是在让一些 SAM 上的节点 从 “没出现过” 变成 “出现过”

考虑在parent树上,可以直接跳,因为一个节点只会从没出现过变成出现过一次,是$O(n)$的

这样就可以方便的维护第一个东西啦

当然,加入一个字符后就把这个点在parent树上的祖先全部+1,这样就维护好第三个操作啦

祖先+1可以看作单点+1,区间查询,可以用BIT维护

复杂度带个BIT的log,不卡常都2000ms跑过去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
#define MAXN 500006

int n , lst , m;

struct SAM{
int son[MAXN][27]; // sons
int par[MAXN] , len[MAXN]; // node
int cnt;
int sz[MAXN];
int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , ecn;
ll num;

void init( ) {
memset( son , 0 , sizeof son ); memset( head , 0 , sizeof head );
cnt = lst = 1; ecn = 0;
}
void ade( int u , int v ) {
nex[++ecn] = head[u] , head[u] = ecn , to[ecn] = v;
}
void addall( ) {
for( int i = 2 ; i <= cnt ; ++ i ) ade( par[i] , i );
}
void ins( int x ) {
int cur = ++ cnt;
len[cur] = len[lst] + 1 , sz[cur] = 1;
int p = lst;
while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
if( !p ) par[cur] = 1;
else {
int q = son[p][x];
if( len[q] == len[p] + 1 ) par[cur] = q;
else {
int cl = ++ cnt;
memcpy( son[cl] , son[q] , sizeof son[q] );
par[cl] = par[q]; // copy
len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
}
}
lst = cur;
}
int L[MAXN] , R[MAXN] , dfn;
void dfs( int u ) {
L[u] = ++ dfn;
for( int i = head[u] ; i ; i = nex[i] )
dfs( to[i] );
R[u] = dfn;
}
int vis[MAXN];
int jump( int u ) {
if( vis[u] ) return 0;
vis[u] = 1;
return len[u] - len[par[u]] + jump( par[u] );
}

int T[MAXN];
void add( int u , int c ) {
while( u < MAXN ) T[u] += c , u += ( u & -u );
}
int sum( int u ) {
int ret = 0;
while( u > 0 ) ret += T[u] , u -= ( u & -u );
return ret;
}

void getit( int u ) {
// printf("get : %d\n",u);
num += jump( u );
add( L[u] , 1 );
}

int mach( char* c , int l ) {
int cur = 1;
for( int i = 0 ; i < l ; ++ i ) {
if( !son[cur][c[i] - 'a'] ) return 0;
else cur = son[cur][c[i] - 'a'];
}
return sum( R[cur] ) - sum( L[cur] - 1 );
}
} S ;

int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , wt[MAXN] , ecn;
void ade( int u , int v , int w ) {
to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn , wt[ecn] = w;
}
int pos[MAXN] , fa[MAXN];
vector<int> ps[MAXN]; int cni;
void bfs( int u ) {
queue<int> Q;
Q.push( u );
while( !Q.empty( ) ) {
int x = Q.front(); Q.pop();
for( int i = head[x] ; i ; i = nex[i] ) {
int v = to[i];
if( fa[x] == v || fa[v] ) continue;
fa[v] = x;
lst = pos[x];
S.ins( wt[i] );
pos[v] = lst , Q.push( v );
ps[cni].push_back( v );
}
}
}

char ch[MAXN]; int bk[MAXN] , ln[MAXN] , id , tot;
char op[MAXN];
struct query {
int opt , id;
} Q[MAXN] ;

int main() {
scanf("%*d");
cin >> n;
for( int i = 1 , u , v ; i < n ; ++ i ) {
scanf("%d%d%s",&u,&v,ch);
ade( u , v , ch[0] - 'a' ) , ade( v , u , ch[0] - 'a' );
}
S.init(); pos[1] = 1;
bfs( 1 );
cin >> m;
int opt , rt , s;
for( int i = 1 ; i <= m ; ++ i ) {
scanf("%d",&opt);
if( opt == 1 ) {
Q[i].opt = 1;
} else if( opt == 2 ) {
Q[i].opt = 2;
++ cni;
scanf("%d%d",&rt,&s);
for( int i = 1 , u , v ; i < s ; ++ i ) {
scanf("%d%d%s",&u,&v,ch);
ade( u , v , ch[0] - 'a' ) , ade( v , u , ch[0] - 'a' );
}
bfs( rt );
Q[i].id = cni;
} else {
scanf("%s",op + tot);
bk[i] = tot , ln[i] = strlen( op + tot );
tot += ln[i];
Q[i].opt = 3;
}
}
Q[0].opt = 2;
S.addall();
S.dfs( 1 );

for( int i = 0 ; i <= m ; ++ i ) {
int o = Q[i].opt;
if( o == 1 ) {
printf("%lld\n",S.num);
} else if( o == 2 ) {
int c = Q[i].id;
for( int j = 0 ; j < ps[c].size() ; ++ j ) {
int u = ps[c][j];
S.getit( pos[u] );
}
} else {
printf("%d\n",S.mach( op + bk[i] , ln[i] ));
}
}
}
文章作者: yijan
文章链接: https://yijan.co/old46/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog