[SDOI2011] 染色
[SDOI2011] 染色 (树链剖分)
题意:
给定一棵 n 个节点的无根树,共有 m 个操作,操作分为两种:
1.将节点 a 到节点 b 的路径上的所有点(包括 a 和 b )都染成颜色 c。
(opt == 'C'
)
2.询问节点 a 到节点 b 的路径上的颜色段数量。
(opt == 'Q'
)
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:11、222、1。
思路:
本题主要思路已经被讲得非常清楚
我提供 求路径上颜色段 の another way
见代码:
int asksum(int p,int l,int r,int askl,int askr,int &tls,int &trs) {
if(askl<=l && r<=askr) {
tls=ls[p]; trs=rs[p];
return sum[p];
}
pushdown(p);
int mid=(l+r)>>1;
bool pl=false,pr=false;
int l1=0,r1=0,l2=0,r2=0,res=0;
if(askl<=mid) {
pl=true;
res+=asksum(p<<1,l,mid,askl,askr,l1,r1);
}
if(mid<askr) {
pr=true;
res+=asksum(p<<1|1,mid+1,r,askl,askr,l2,r2);
}
if(pl==true && pr==true) {
if(r1==l2) res--;
tls=l1; trs=r2;
}
else if(pl==true) {
tls=l1; trs=r1;
}
else if(pr==true) {
tls=l2; trs=r2;
}
return res;
}
int ask(int x,int y) {
int lca=getlca(x,y);
int ll,rr,last1=0,last2=0,res1=0,res2=0,res=0;
while(top[x]!=top[lca]) {
ll=0; rr=0;
res1+=asksum(1,1,n,dfn[top[x]],dfn[x],ll,rr);
if(!last1) last1=ll;
else res1=res1-(rr==last1),last1=ll;
x=fa[top[x]];
}
if(lca!=x) {
ll=0; rr=0;
res1+=asksum(1,1,n,dfn[lca]+1,dfn[x],ll,rr);
if(last1) res1=res1-(rr==last1);
last1=ll;
}
while(top[y]!=top[lca]) {
ll=0; rr=0;
res2+=asksum(1,1,n,dfn[top[y]],dfn[y],ll,rr);
if(!last2) last2=ll;
else res2=res2-(rr==last2),last2=ll;
y=fa[top[y]];
}
ll=0; rr=0;
res2+=asksum(1,1,n,dfn[lca],dfn[y],ll,rr);
if(last2) res2=res2-(rr==last2);
last2=ll;
res=res1+res2-(last1==last2);
return res;
}
对于上方的路径,我们将其分为green和blue两部分路径,对它们分别统计颜色数:
这两条路径,每一条会被不同的重轻链分割成很多部分
每个部分对应线段树上の一个区间
将这些区间合并时,要记录区间左右端点的颜色是什么
我选择在查找的时候记录它们(使用 asksum(......int &tls,int &trs) )
用 last1 (green part) 和 last2 (blue part) 记录上一个区间的左端点,并拿它与当前区间的右端点作比较
若二者相同,则res--,反之res不变
green and blue parts统计完成后,将last1与last2作比较,相同则res--,反之res不变
最后输出res即可