Description
神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
Input
输入第一行为两个整数n和m,分别代表树上苹果的个数和前来膜拜的人数。
接下来的一行包含n个数,第i个数代表编号为i的苹果的颜色Coli。
接下来有n行,每行包含两个数x和y,代表有一根树枝连接了苹果x和y(或者根和一个苹果)。
接下来有m行,每行包含四个整数u、v、a和b,代表这个人要数苹果u到苹果v的颜色种数,同时这个人认为颜色a就是颜色b。如果a=b=0,则代表这个人没有患色盲症。
Output
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
Sample Input
5 3
1 1 3 3 2
0 1
1 2
1 3
2 4
3 5
1 4 0 0
1 4 1 3
1 4 1 2
Sample Output
2
1
2
HINT
0<=x,y,a,b<=N
N<=50000
1<=U,V,Coli<=N
距离上一次做这道题已经过了1个月了,不得不感慨当年还是太年轻。
这道题可以算作树链剖分的裸题,注意一下端点颜色相同的线段的合并就行了。
*第一次做题时一直没有想通query时swap(u,v)之后u所在的链和v所在的链的链顶颜色(cet[1]和cet[2])怎样保持正确性。其实很简单,在swap(u,v)后swap(cet[1],cet[2])就可以啦。
*swap的条件 if(dep[top[u]] < dep[top[v]] )
/**************************************************************
Problem: 2243
User: DD_D
Language: C++
Result: Accepted
Time:5168 ms
Memory:14548 kb
****************************************************************/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m;
int col[N];
int to[2*N],nxt[2*N],head[N],etot;
int dep[N],son[N],size[N],fat[N];
int in[N],out[N],seq[N],idc,top[N];
int cet[5];
struct node{
node *ls,*rs;
int cof,cor,fg,num;
void pushdown(){
if(fg!=-1){
//把它底下的要更新了
ls->fg=rs->fg=fg;
ls->cof=ls->cor=rs->cof=rs->cor=fg;
ls->num=1;rs->num=1;
fg=-1;
}
}
void update(){
cof=ls->cof;
cor=rs->cor;
if(ls->cor==rs->cof) num=ls->num+rs->num-1;
else num=ls->num+rs->num;
}
}pool[2*N+5],*tail=pool,*rt;
void adde(int u,int v)
{
to[++etot]=v;
nxt[etot]=head[u];
head[u]=etot;
}
void dfs1(int u,int fa)//第一遍求siz fat dep 重儿子
{
size[u]=1;
fat[u]=fa;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
dep[v]=dep[u]+1;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]||son[u]==-1) son[u]=v;
}
}
void dfs2(int u,int tp)//第二遍求dfs序 求dfs序时先进入重儿子 top[]
{
in[u]=++idc;
seq[idc]=u;
top[u]=tp;
if(son[u]!=-1) dfs2(son[u],tp);
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==son[u]||v==fat[u]) continue;
dfs2(v,v);
}
out[u]=idc;
}
node *build(int lf,int rg)
{
node *nd=++tail;
if(lf==rg) {
nd->cof=nd->cor=col[seq[lf]];
nd->fg=-1;
nd->num=1;
return nd;
}
int mid=(lf+rg)>>1;
nd->ls=build(lf,mid);
nd->rs=build(mid+1,rg);
nd->fg=-1;//!!!!!
nd->update();
return nd;
}
void modify(node *nd,int L,int R,int lf,int rg,int c)
{
if(L<=lf&&rg<=R){
nd->fg=c;
nd->cof=c;
nd->cor=c;
nd->num=1;//!!!!
return;
}
int mid=(lf+rg)>>1;
nd->pushdown();
if(L<=mid) modify(nd->ls,L,R,lf,mid,c);
if(R>mid) modify(nd->rs,L,R,mid+1,rg,c);
nd->update();
}
void modify(int u,int v,int c)
{
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
modify(rt,in[top[u]],in[u],1,n,c);
u=fat[top[u]];
}
if(dep[u]<dep[v]) swap(u,v);
modify(rt,in[v],in[u],1,n,c);
}
int query(node *nd,int L,int R,int lf,int rg)
{
if(L<=lf&&rg<=R){
if(L==lf) cet[0]=nd->cof;
if(R==rg) cet[3]=nd->cor;
return nd->num;
}
int mid=(lf+rg)>>1;
nd->pushdown();
int ans=0,r=-1,l=-1;
if(L<=mid){
ans+=query(nd->ls,L,R,lf,mid);
l=nd->ls->cor;
}
if(R>mid) {
ans+=query(nd->rs,L,R,mid+1,rg);
r=nd->rs->cof;
}
nd->update();
if(l==r) ans--;
return ans;
}
int query(int u,int v)
{
int ans=0;
memset(cet,-1,sizeof(cet));
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
swap(cet[1],cet[2]);
}
ans+=query(rt,in[top[u]],in[u],1,n);
u=fat[top[u]];
if(cet[3]==cet[1]) ans--;
cet[1]=cet[0];
}
if(dep[u]<dep[v]) {
swap(u,v);
swap(cet[1],cet[2]);
}
ans+=query(rt,in[v],in[u],1,n);
if(cet[1]==cet[3]) ans--;
if(cet[2]==cet[0]) ans--;
return ans;
}
int main()
{
memset(son,-1,sizeof(son));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&col[i]);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
adde(u,v),adde(v,u);
}
dfs1(1,1);
dfs2(1,1);
rt=build(1,idc);
while(m--){
char ch;
int a,b,c;
scanf("\n%c",&ch);
if(ch=='C'){
scanf("%d%d%d",&a,&b,&c);
modify(a,b,c);
}
if(ch=='Q'){
scanf("%d%d",&a,&b);
printf("%d\n",query(a,b));
}
}
return 0;
}
后记:突然发现博客里第一篇文章说的“想写但没过的树链剖分“就是这一道,缘分啊,真真妙不可言~