CF1017G The Tree

怎么都没人写好写又好懂的官方题解的做法?

考虑对询问分块。

设块大小为 $s$ ,那么每一个询问块用到过的点的数量是 $O(s)$ 的。

考虑把询问的这 $s$ 个点拿出来建虚树(这里可以 $O(n)$ 建虚树的)。我们对于一个虚树上的边,存一下这边在原树上的点的个数,白色点的个数。

然后考虑第一个操作,我们可以直接在新树上模拟进行操作。具体来说,修改一个点,如果这个点是白色直接修改,否则查一下当前操作这个点的数量是否足以递归进一个子树。递归操作即可。说白了,对关键点修改直接改,对非关键点延到块处理完了再改。复杂度是 $O(s)$。

考虑第二个操作,暴力修改子树,把每条边上白色点的个数变成原树上点的个数即可。复杂度 $O(s)$。

考虑询问,直接输出这个点的颜色即可。复杂度 $O(1)$。

考虑对一个块操作完了,我们遍历这个树,对于每个点都可以通过类似建立虚树时的办法把每个未被询问的点的颜色还原。

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
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 200006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , q;
int A[MAXN] , fa[MAXN];

vi G[MAXN];
int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , wto[MAXN << 1] , dto[MAXN << 1] , ecn;
void ade( int u , int v , int w , int ww ) {
to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn , wto[ecn] = w , dto[ecn] = ww;
}

int mark[MAXN] , black[MAXN];
void afs( int u , int f , int w , int d ) {
if( mark[u] ) {
if( f != -1 ) ade( f , u , w , d );
for( int& v : G[u] ) afs( v , u , 0 , 0 );
} else {
if( !black[u] ) ++ w;
for( int& v : G[u] ) afs( v , f , w , d + 1 );
}
}

int pu[MAXN];
void rever( int u ) {
if( !black[u] ) return void( black[u] = 1 );
++ pu[u];
for( int i = head[u] ; i ; i = nex[i] ) if( wto[i] + 1 <= pu[u] )
rever( to[i] );
}

int cov[MAXN]; // the whole subtree is white
void cover( int u ) {
black[u] = pu[u] = 0;
cov[u] = 1;
for( int i = head[u] ; i ; i = nex[i] ) wto[i] = dto[i] , cover( to[i] );
}

int re[MAXN];
void cfs( int u , int p , int cl ) {
if( mark[u] )
p = pu[u] , cl |= cov[u];
else {
black[u] = re[u];
if( cl ) black[u] = 0;
if( !black[u] && p ) black[u] = 1 , -- p;
}
for( int v : G[u] ) cfs( v , p , cl );
}

int op[MAXN] , pt[MAXN];

void solve() {
cin >> n >> q;
int blo = sqrt( q );
int u;
rep( i , 2 , n ) scanf("%d",&u) , fa[i] = u , G[u].pb( i );
rep( i , 1 , q ) scanf("%d%d",op + i,pt + i);
int s = ( q - 1 ) / blo + 1;
rep( i , 1 , s ) {
int l = ( i - 1 ) * blo + 1 , r = min( q , i * blo );
rep( i , 1 , n ) re[i] = black[i] , mark[i] = cov[i] = pu[i] = head[i] = 0;
rep( i , l , r ) mark[pt[i]] = 1;
afs( 1 , -1 , 0 , 0 );
rep( i , l , r ) {
if( op[i] == 1 ) rever( pt[i] );
else if( op[i] == 2 ) cover( pt[i] );
else puts( black[pt[i]] ? "black" : "white" );
}
cfs( 1 , 0 , 0 );
}
}

signed main() {
// int T;cin >> T;while( T-- ) solve();
solve();
}


文章作者: yijan
文章链接: https://yijan.co/cf1017g-the-tree/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog