传送门1
传送门2
写在前面:比一些裸题好多了……
思路:典型的树链剖分,不过我们要存储的是每段区间内颜色段数量,对于这个问题,显然我们要存下整个区间的详细状态是不可能的,但可以把这个区间的左右端点记录一下,中间的状态无所谓,只要记录区间颜色段总数就行了,因为我们要对两个相邻区间a,b操作时,他们的相接点,即Ra与Lb的颜色是否相同会影响总区间的颜色段数量,内部的颜色并不会影响总区间的颜色段数量。这样一来建线段树和改变区间颜色的问题就比较好办了,对左右子树分别操作后pushup上来,再通过对左子树最右端和右子树最左端是否相同计算总区间数量就行了。但是我们发现解决询问操作时,由于是按照轻重链一步步往上跑,并且由于lazy标记的关系,所以我们不能快速判断两条链相邻端点间的颜色关系,所以我们还需要一个函数来查询两相邻节点的颜色。总的来说,这道题比其他裸链剖题的思考要深一些,充分利用lazy思想,通过判断和改变左右端点的颜色从而调整整个区间的状态
注意:
1.codevs数据范围有误,n<=10^5而不是10^4
2.我的颜色从1开始算,因为lazy初始为0……
代码:
#include<bits/stdc++.h>
using namespace std;
int tot,cnt,n,m,a,b,c;
char ch;
int top[100010],fa[100010],son[100010],dep[100010],siz[100010],first[100010],pre[100010],L[100010],color[100010];
struct os
{
int u,v,next;
}e[200010];
struct node
{
int sum,lazy;
}tree[800010];
void pushdown(int now,int l,int r)
{
if (!tree[now].lazy) return;
int mid=(l+r)>>1;
color[pre[mid]]=tree[now<<1].lazy=tree[now].lazy;
color[pre[mid+1]]=tree[now<<1|1].lazy=tree[now].lazy;
tree[now<<1].sum=tree[now<<1|1].sum=1;
tree[now].lazy=0;
}
void add(int x,int y)
{
e[++tot].u=x;
e[tot].v=y;
e[tot].next=first[x];
first[x]=tot;
}
void dfs1(int now)
{
siz[now]=1;
for (int i=first[now];i;i=e[i].next)
if (e[i].v!=fa[now])
{
dep[e[i].v]=dep[now]+1;
fa[e[i].v]=now;
dfs1(e[i].v);
if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
siz[now]+=siz[e[i].v];
}
}
void dfs2(int now,int tp)
{
top[now]=tp;
L[now]=++cnt;
pre[cnt]=now;
if (son[now]) dfs2(son[now],tp);
for (int i=first[now];i;i=e[i].next)
if (e[i].v!=son[now]&&e[i].v!=fa[now]) dfs2(e[i].v,e[i].v);
}
void build(int now,int begin,int end)
{
if (begin==end)
{
tree[now].sum=1;
return;
}
int mid=(begin+end)>>1;
build(now<<1,begin,mid);
build(now<<1|1,mid+1,end);
tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
}
void update(int now,int begin,int end,int l,int r,int num)
{
if (l<=begin&&end<=r)
{
color[pre[begin]]=color[pre[end]]=num;
tree[now].lazy=num;
tree[now].sum=1;
return;
}
pushdown(now,begin,end);
int mid=(begin+end)>>1;
if (mid>=l) update(now<<1,begin,mid,l,r,num);
if (mid<r) update(now<<1|1,mid+1,end,l,r,num);
tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
}
int get(int now,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return tree[now].sum;
pushdown(now,begin,end);
int mid=(begin+end)>>1,ans=0;
if (mid>=l) ans+=get(now<<1,begin,mid,l,r);
if (mid<r) ans+=get(now<<1|1,mid+1,end,l,r);
if (l<=mid&&mid<r) ans-=(color[pre[mid]]==color[pre[mid+1]]);
return ans;
}
int judge(int now,int begin,int end,int pos)
{
if (begin==end) return color[pre[pos]];
pushdown(now,begin,end);
int mid=(begin+end)>>1,ans=0;
if (mid>=pos) ans=judge(now<<1,begin,mid,pos);
else ans=judge(now<<1|1,mid+1,end,pos);
tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum-(color[pre[mid]]==color[pre[mid+1]]);
return ans;
}
void solve1(int l,int r)
{
int ans=0,f1=top[l],f2=top[r];
while (f1!=f2)
{
if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
ans+=get(1,1,cnt,L[f1],L[l]);
l=fa[f1];
ans-=(judge(1,1,cnt,L[f1])==judge(1,1,cnt,L[l]));
f1=top[l];
}
if (dep[l]>dep[r]) swap(l,r);
ans+=get(1,1,cnt,L[l],L[r]);
printf("%d\n",ans);
}
void solve2(int l,int r,int num)
{
int f1=top[l],f2=top[r];
while (f1!=f2)
{
if (dep[f1]<dep[f2]) swap(f1,f2),swap(l,r);
update(1,1,cnt,L[f1],L[l],num);
l=fa[f1];
f1=top[l];
}
if (dep[l]>dep[r]) swap(l,r);
update(1,1,cnt,L[l],L[r],num);
}
main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&color[i]),color[i]++;
for (int i=1;i<n;i++)
scanf("%d%d",&a,&b),
add(a,b),add(b,a);
dfs1(1);
dfs2(1,1);
build(1,1,cnt);
while (m--)
{
ch=getchar();
while (ch!='C'&&ch!='Q') ch=getchar();
if (ch=='C')
{
scanf("%d%d%d",&a,&b,&c);
solve2(a,b,++c);
}
else
{
scanf("%d%d",&a,&b);
solve1(a,b);
}
}
}