BZOJ4817 [SDOI2017]树点涂色

Bob有一棵$n$个点的有根树,其中$1$号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:

  • 1 x 把点$x$到根节点的路径上所有的点染上一种没有用过的新颜色。
  • 2 x y 求$x$到$y$的路径的权值。
  • 3 x 在以$x$为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行$m$次操作。
$n,m leqslant 10^5$。

题解

这道题我是按着LCT的标签找的,然而看到这道题的时候满脑子都是树剖……根本看不出哪里要用LCT……个人觉得这题还是蛮巧妙的。
首先,如果我们把同一种颜色的点放到LCT的一棵Splay中去,那么可以发现,1操作恰好就是LCT的access操作了。
对于一个点,他到根节点的权值,也就是LCT中这个点到根节点的实链的条数。记$f[i]$表示$i$号节点到根的权值。由这个权值的性质,我们不难得到,对于两个点$x$与$y$,$x$到$y$的路径上的权值为$$f[x]+f[y]-2f[lca(x,y)]+1$$
于是第二个操作也解决了。
第三个操作,我们不难想到把dfs序抽出来,在线段树上查询即可。
最后我们还有一个问题,也就是$f$数组该如何维护。其实不难想到,在access操作把一条虚边变为实边,我们就把这条虚边对应的子树在线段树上$-1$,若将一条实边变为虚边,就把实边对应的子树在线段树上$+1$。于是问题得到了解决。
时间复杂度$O(nlog^2n)$。
代码实现上遇到了一些麻烦,这里讲一下我写的时候遇到的几个错误:

  • 注意线段树初始化的值为这个点在原树上的深度+1。
  • 还是原树与辅助树的问题,access操作时,当我们要对线段树上修改的时候,假如我们在Splay上要把实边$u$到$v$去掉,在线段树上修改的时候,并不是直接把$dfn[v]$到$dfn[v]+size[v]-1$这一段的答案$+1$,而应该找到$v$点所在的Splay中一直往左走,走到这棵Splay中深度最浅的点,再在线段树上进行修改。

代码

      
      
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
      
      
using namespace std;
typedef long long ll;
const int INF= 1e9;
const double eps= 1e-9;
const int maxn= 1e5+ 10;
int to[maxn<< 1],nex[maxn<< 1],beg[maxn],dfn[maxn],id[maxn],f[maxn][ 20],dep[maxn],size[maxn];
int e,dfs_time,n;
inline int (){
int x= 0,flag= 1;
char ch=getchar();
while(! isdigit(ch) && ch!= '-')ch=getchar();
if(ch== '-')flag= -1,ch=getchar();
while( isdigit(ch))x=(x<< 3)+(x<< 1)+ch- '0',ch=getchar();
return x*flag;
}
struct Seg_T{
int Seg[maxn<< 2],Tag[maxn<< 2];
void Build(int h,int l,int r){
if(l==r){
Seg[h]=dep[id[l]];
return;
}
int mid=(l+r)>> 1;
Build(h<< 1,l,mid),Build(h<< 1| 1,mid+ 1,r);
Seg[h]=max(Seg[h<< 1],Seg[h<< 1| 1]);
}
inline void Push_Down(int h){
if(Tag[h]){
Tag[h<< 1]+=Tag[h],Seg[h<< 1]+=Tag[h];
Tag[h<< 1| 1]+=Tag[h],Seg[h<< 1| 1]+=Tag[h];
Tag[h]= 0;
}
}
void Modify(int h,int l,int r,int L,int R,int val){
if(L<=l && r<=R){
Seg[h]+=val,Tag[h]+=val;
return;
}
Push_Down(h);
int mid=(l+r)>> 1;
if(L<=mid)Modify(h<< 1,l,mid,L,R,val);
if(R>mid)Modify(h<< 1| 1,mid+ 1,r,L,R,val);
Seg[h]=max(Seg[h<< 1],Seg[h<< 1| 1]);
}
int Query(int h,int l,int r,int L,int R){
if(L<=l && r<=R) return Seg[h];
Push_Down(h);
int mid=(l+r)>> 1,ans= 0;
if(L<=mid)ans=max(ans,Query(h<< 1,l,mid,L,R));
if(R>mid)ans=max(ans,Query(h<< 1| 1,mid+ 1,r,L,R));
return ans;
}
}Seg;
struct LCT{
#define rel(x) (ch[fa[x]][1]==x)
int ch[maxn][ 2],fa[maxn];
inline int isroot(int x){
return ch[fa[x]][ 0]!=x && ch[fa[x]][ 1]!=x;
}
inline void Rotate(int x){
int y=fa[x],t=fa[y],flag=rel(x);
ch[fa[ch[x][flag^ 1]]=y][flag]=ch[x][flag^ 1];
fa[x]=t; if(!isroot(y))ch[t][rel(y)]=x;
ch[fa[y]=x][flag^ 1]=y;
}
inline void Splay(int x){
for(;!isroot(x);Rotate(x))
if(!isroot(fa[x]))Rotate(rel(x)^rel(fa[x])?x:fa[x]);
}
inline int Getl(int x){
while(ch[x][ 0])x=ch[x][ 0];
return x;
}
inline void Access(int x){
for( int i= 0;x;x=fa[i=x]){
Splay(x);
if(ch[x][ 1]){
int pos=Getl(ch[x][ 1]);
Seg.Modify( 1, 1,n,dfn[pos],dfn[pos]+size[pos] -1, 1);
}
ch[x][ 1]=i;
if(i){
int pos=Getl(i);
Seg.Modify( 1, 1,n,dfn[pos],dfn[pos]+size[pos] -1, -1);
}
}
}
}T;
inline void add(int x,int y){
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
}
void dfs(int x,int fa){
int i;
dfn[x]=++dfs_time;id[dfs_time]=x;
f[x][ 0]=fa;T.fa[x]=fa;size[x]= 1;
for(i=beg[x];i;i=nex[i]){
if(to[i]==fa) continue;
dep[to[i]]=dep[x]+ 1;
dfs(to[i],x);
size[x]+=size[to[i]];
}
}
inline int Get_LCA(int x,int y){
int i;
if(dep[x]<dep[y])swap(x,y);
for(i= 18;i>= 0;i--)
if((dep[x]-dep[y]) & ( 1<<i))
x=f[x][i];
if(x==y) return x;
for(i= 18;i>= 0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][ 0];
}
int main(){
int i,j,q;
#ifndef ONLINE_JUDGE
freopen( "BZOJ4817.in", "r", stdin);
freopen( "BZOJ4817.out", "w", stdout);
#endif
n=read();q=read();
for(i= 1;i<n;i++){
int x=read(),y=read();
add(x,y);add(y,x);
}
dep[ 1]= 1;dfs( 1, 0);
for(i= 1;i<= 18;i++)
for(j= 1;j<=n;j++)
f[j][i]=f[f[j][i -1]][i -1];
Seg.Build( 1, 1,n);
while(q--){
int opt=read();
if(opt== 1){
int x=read();
T.Access(x);
}
if(opt== 2){
int x=read(),y=read(),lca=Get_LCA(x,y);
int ans=Seg.Query( 1, 1,n,dfn[x],dfn[x])+Seg.Query( 1, 1,n,dfn[y],dfn[y]);
printf( "%dn",ans -2*Seg.Query( 1, 1,n,dfn[lca],dfn[lca])+ 1);
}
if(opt== 3){
int x=read();
printf( "%dn",Seg.Query( 1, 1,n,dfn[x],dfn[x]+size[x] -1));
}
}
return 0;
}

原文:大专栏  BZOJ4817 [SDOI2017]树点涂色


猜你喜欢

转载自www.cnblogs.com/sanxiandoupi/p/11657788.html