给出一颗 n n n个结点的无根树,处理 m m m个操作,每个操作只会是如下两种之一:
(1)将结点 a a a到 b b b的路径上的所有点染成颜色 k k k
(2)询问结点 a a a到结点 b b b的简单路径的颜色段数量
对于路径 ( x , y ) (x,y) (x,y),先 s p l i t ( x , y ) split(x,y) split(x,y);
我们先考虑查询,
s
u
m
sum
sum保存当前
s
p
l
a
y
splay
splay的“连接处”的个数,即相邻颜色不一样的节点对数,答案即
t
r
e
e
[
y
]
.
s
u
m
+
1
tree[y].sum+1
tree[y].sum+1,那么问题就到了如何在
p
u
s
h
_
u
p
push\_up
push_up中维护这个值,显然,对节点
x
x
x,
t
r
e
e
[
x
]
.
s
u
m
=
t
r
e
e
[
s
o
n
[
0
]
]
.
s
u
m
+
t
r
e
e
[
s
o
n
[
1
]
]
.
s
u
m
+
x
tree[x].sum=tree[son[0]].sum+tree[son[1]].sum+x
tree[x].sum=tree[son[0]].sum+tree[son[1]].sum+x节点对他前后两段形成的“连接处个数”,一种方法是找前驱,即用传统
s
p
l
a
y
splay
splay的方法,但在
L
C
T
LCT
LCT内由于
p
r
e
(
)
pre()
pre()会调用
s
p
l
a
y
(
)
splay()
splay(),
s
p
l
a
y
(
)
splay()
splay()会调用$push_up()
,
,
,push_up
会调用
会调用
会调用pre()
,循环递归,会出现错误。另一种方法是新增成员,来保存当前
,循环递归,会出现错误。另一种方法是新增成员,来保存当前
,循环递归,会出现错误。另一种方法是新增成员,来保存当前splay
子树的最左端点和最右端点,这样贡献即
子树的最左端点和最右端点,这样贡献即
子树的最左端点和最右端点,这样贡献即tree[x].color!=tree[tree[x].son[i]].colorr[i]
,此时
,此时
,此时reverse()
也需要反转
也需要反转
也需要反转tree[x].colorr[i]$
修改操作只需 s p l i t ( x , y ) split(x,y) split(x,y)之后在 y y y处下放标记即可了
#include
#define ll long long
using namespace std;
int n,q;
struct LCT
{
struct node
{
int color,tag,sum,son[2],fa,lazy,colorr[2];
};
stack<int>s;
vector<node>tree;
LCT():tree(200005){}
inline bool isroot(int x)
{
return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
}
inline int getson(int x,int y)
{
return x==tree[y].son[1];
}
inline void push_up(int x)
{
tree[x].sum=0;
for(int i=0;i<=1;i++)
{
if(tree[x].son[i])
{
tree[x].sum+=tree[tree[x].son[i]].sum+(tree[x].color!=tree[tree[x].son[i]].colorr[!i]);
tree[x].colorr[i]=tree[tree[x].son[i]].colorr[i];
}
else tree[x].colorr[i]=tree[x].color;
}
}
inline void rotate(int x)
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
tree[x].fa=z;
if(!isroot(y)) tree[z].son[k2]=x;
tree[y].son[k1]=tree[x].son[!k1];
if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
tree[x].son[!k1]=y;
tree[y].fa=x;
push_up(y); push_up(x);
}
inline void reverse(int x)
{
swap(tree[x].son[0],tree[x].son[1]);
swap(tree[x].colorr[0],tree[x].colorr[1]);
tree[x].tag^=1;
}
inline void ccolor(int x,int k)
{
tree[x].lazy=k;
tree[x].color=k;
tree[x].sum=0;
tree[x].colorr[0]=tree[x].colorr[1]=k;
}
void push_down(int x)
{
if(tree[x].tag)
{
for(int i=0;i<=1;i++)
if(tree[x].son[i]) reverse(tree[x].son[i]);
tree[x].tag=0;
}
if(tree[x].lazy)
{
for(int i=0;i<=1;i++)
if(tree[x].son[i]) ccolor(tree[x].son[i],tree[x].lazy);
tree[x].lazy=0;
}
}
void splay(int x)
{
int now=x;
s.push(now);
while(!isroot(now))
{
s.push(tree[now].fa);
now=tree[now].fa;
}
while(!s.empty())
{
push_down(s.top());
s.pop();
}
while(!isroot(x))
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
if(!isroot(y))
{
if(k1==k2) rotate(y);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x)//打通root->x的splay
{
int last=0;
while(x)
{
splay(x);
tree[x].son[1]=last;
push_up(last=x);
x=tree[x].fa;
}
}
inline void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
inline int findroot(int x)
{
access(x);
splay(x);
while(tree[x].son[0])
{
push_down(x);
x=tree[x].son[0];
}
splay(x);
return x;
}
inline void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
inline void link(int x,int y)
{
makeroot(x);
tree[x].fa=y;
}
}lct;
int main()
{
cin>>n>>q;
int nn=n;
for(int i=1;i<=n;i++) scanf("%d",&lct.tree[i].color);
for(int i=1;i<n;i++)
{
int u,v; scanf("%d%d",&u,&v);
lct.link(u,v);
}
while(q--)
{
// lct.dfs();
char s[5]; scanf("%s",s+1);
if(s[1]=='Q')
{
int x,y; scanf("%d%d",&x,&y);
lct.split(x,y);
printf("%d\n",lct.tree[y].sum+1);
}
else
{
int x,y,k; scanf("%d%d%d",&x,&y,&k);
lct.split(x,y); lct.ccolor(y,k);
}
}
return 0;
}