Description
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
Input
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
Output
对于每个询问操作,输出一行答案。
既然是关于路径的那么直接去想树链剖分没跑了。
如何快速的统计一个区间以及对一个区间进行合并呢,我们需要在线段树上维护三个信息,第一个是这个区间有多少种不同的颜色,另外两个就是两边分别是什么颜色,这样如果颜色相同,两个区间合并时就是两个区间个数加和-1,否则就是两个区间不同颜色个数加和了。
对于染色,在染色的时候树链剖分时相当于执行线段树的区间赋值操作即可。
对于查询时要稍微注意一下细节,由于我们树链剖分时,两个点都在向上走,且此时不能将两个答案合并,所以我们要先对两边分别计算当前答案,最后合并。由于底层的dfs序大,所以向上时在与新的答案合并的时候,要将原来的答案左边与新的答案的右边进行比较,来更新新的答案,如果写了合并函数,就等同于将原答案放在合并函数的右边。在最后两个答案在一条链上之时,我们先将两个点之间的区间,用上述方法和相对在底下的区间进行合并,得到ans1,相对较高的点已经得到的答案为ans2,那么此时两个区间的左端点是在一块的,所以按上述合并方法判断两个左端点颜色是否相同即可。讲的没有画的好,建议自己手推一下非常明白的。
下附AC代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define maxn 100005
#define lson (now<<1)
#define rson ((now<<1)|1)
#define mid ((nl+nr)>>1)
using namespace std;
//以下为基本数组
struct nod
{
int sum,l,r;
nod()
{
sum=0;
l=-1;
r=-1;
}
};
nod bing(nod a,nod b)
{
nod ans;
ans.l=a.l;
ans.r=b.r;
ans.sum=a.sum+b.sum;
if(a.r==b.l)
ans.sum--;
return ans;
}
int n,m;
int tt[maxn];
char s[20];
//以下为线段树
nod dat[maxn<<2];
int lazy[maxn<<2];
void pushup(int now)
{
dat[now]=bing(dat[lson],dat[rson]);
return;
}
void pushdown(int now)
{
if(lazy[now]!=-1)
{
dat[lson].sum=1;
dat[rson].sum=1;
dat[lson].l=dat[lson].r=dat[rson].l=dat[rson].r=lazy[now];
lazy[lson]=lazy[now];
lazy[rson]=lazy[now];
lazy[now]=-1;
}
return;
}
void update(int ql,int qr,int col,int nl,int nr,int now)
{
if(ql<=nl && nr<=qr)
{
dat[now].sum=1;
dat[now].l=dat[now].r=col;
lazy[now]=col;
return;
}
pushdown(now);
if(ql<=mid) update(ql,qr,col,nl,mid,lson);
if(mid<qr) update(ql,qr,col,mid+1,nr,rson);
pushup(now);
}
nod query(int ql,int qr,int nl,int nr,int now)
{
if(ql<=nl && nr<=qr)
{
return dat[now];
}
nod ans;
pushdown(now);
int flag1=0,flag2=0;
nod temp1,temp2;
if(ql<=mid)
{
temp1=query(ql,qr,nl,mid,lson);
flag1=1;
}
if(mid<qr)
{
temp2=query(ql,qr,mid+1,nr,rson);
flag2=1;
}
pushup(now);
if(flag1==1 && flag2==1)
return bing(temp1,temp2);
if(flag1==1)
return temp1;
return temp2;
}
//以下为树链剖分
int cnt;
int dfn[maxn];
vector<int>edge[maxn];
int fa[maxn],anc[maxn],siz[maxn],dep[maxn];
void dfs1(int now,int pa)
{
int len=edge[now].size();
siz[now]=1;
fa[now]=pa;
for(int i=0;i<len;i++)
{
int nex=edge[now][i];
if(nex!=pa)
{
dep[nex]=dep[now]+1;
dfs1(nex,now);
siz[now]+=siz[nex];
}
}
}
void dfs2(int now,int top)
{
dfn[now]=++cnt;
anc[now]=top;
int len=edge[now].size();
int son=0;
for(int i=0;i<len;i++)
{
int nex=edge[now][i];
if(siz[nex]>siz[son] && nex!=fa[now])
{
son=nex;
}
}
if(son!=0)
dfs2(son,top);
for(int i=0;i<len;i++)
{
int nex=edge[now][i];
if(nex!=son && nex!=fa[now])
{
dfs2(nex,nex);
}
}
}
void update(int p,int q,int col)
{
int f1=anc[p],f2=anc[q];
while(f1!=f2)
{
if(dep[f1]>=dep[f2])
{
update(dfn[f1],dfn[p],col,1,n,1);
p=fa[f1];
}
else
{
update(dfn[f2],dfn[q],col,1,n,1);
q=fa[f2];
}
f1=anc[p];f2=anc[q];
}
if(dfn[p]>dfn[q]) swap(p,q);
update(dfn[p],dfn[q],col,1,n,1);
}
int query(int p,int q)
{
nod ans1=query(dfn[p],dfn[p],1,n,1),ans2=query(dfn[q],dfn[q],1,n,1);
int f1=anc[p],f2=anc[q];
while(f1!=f2)
{
if(dep[f1]>=dep[f2])
{
nod now=query(dfn[f1],dfn[p],1,n,1);
ans1=bing(now,ans1);
p=fa[f1];
}
else
{
nod now=query(dfn[f2],dfn[q],1,n,1);
ans2=bing(now,ans2);
q=fa[f2];
}
f1=anc[p];f2=anc[q];
}
if(dfn[p]>dfn[q])
{
swap(p,q);
swap(ans1,ans2);
}
nod now=query(dfn[p],dfn[q],1,n,1);
ans2=bing(now,ans2);
return ans1.sum+ans2.sum-(ans1.l==ans2.l);
}
int main()
{
memset(lazy,-1,sizeof(lazy));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&tt[i]);
}
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
edge[x].push_back(y);
edge[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++)
{
update(dfn[i],dfn[i],tt[i],1,n,1);
}
while(m--)
{
scanf("%s",s);
if(s[0]=='C')
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
update(x,y,z);
}
else
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y));
}
}
}