题目传送门:【BZOJ 2243】
题目大意:给定一棵有 n 个节点的无根树和 m 个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
题目分析:
(说好的是一道模板题,结果花了本人 4 个小时修改之后的线段树)
如题,通常来讲,询问一棵树上的节点信息这样的题,并且包含着“询问两个不同节点的路径上的信息”时,基本上就可以确定是树链剖分的题了。
所以这道题就是道实实在在的树链剖分模板。
但是!
这道题比较坑的地方在于不同颜色的合并,合并时我们用线段树来进行操作(当然是树链剖分之后的线段树),这时就要注意重复的问题。如果左子树最右边的点的颜色和右子树最左边的点颜色相同,那么,合并之后的颜色区块数量就要-1。所以我们需要单独记录下左右两个端点的颜色信息。
同样还有一个细节:颜色的区间为 [ 0 , 10 9 ],所以区间修改的标记在初始化时应为 -1。
至于最后一个问题嘛……有人问为什么一直 RE……
很可能是你的输入操作 ‘C’ 和 ‘Q’ 这个地方有问题……
用字符串输入就行啦
下面附上代码(附注释):
- #include<cstdio>
- #include<algorithm>
- #define ls (nd<<1)
- #define rs (nd<<1|1)
- #define Root 1
- using namespace std;
- const int MX=100005;
- struct Edge{
- int to,next;
- }edge[MX*2];
- int n,m,now=0,_index=0;
- int line[MX],head[MX];
- int depth[MX],size[MX],fa[MX],son[MX],top[MX],in[MX],seq[MX];
- struct SegTree{
- int size,flag; //size:不同颜色块的数量 flag:新颜色
- int l,r; //l,r:两端颜色
- }seg[MX*4];
- /*————– ↓树链剖分↓ ————–*/
- inline void adde(int u,int v){
- edge[++now].to=v;
- edge[now].next=head[u];
- head[u]=now;
- }
- void dfs1(int u){
- size[u]=1;
- for (int i=head[u];i;i=edge[i].next){
- int v=edge[i].to;
- if (fa[u]==v) continue;
- fa[v]=u;
- depth[v]=depth[u]+1;
- dfs1(v);
- if (size[v]>size[son[u]]) son[u]=v;
- size[u]+=size[v];
- }
- }
- void dfs2(int u,int tp){
- top[u]=tp;
- seq[++_index]=u;
- in[u]=_index;
- if (son[u]) dfs2(son[u],tp);
- for (int i=head[u];i;i=edge[i].next){
- int v=edge[i].to;
- if (fa[u]==v || son[u]==v) continue;
- dfs2(v,v);
- }
- }
- /*————– ↓线段树↓ ————–*/
- void build(int nd,int lf,int rt){
- seg[nd].flag=-1; //flag可以为0,因此设为-1保证正确性
- if (lf==rt){
- seg[nd].size=1;
- seg[nd].l=seg[nd].r=line[seq[lf]];
- } else {
- int mid=(lf+rt)>>1;
- build(ls,lf,mid);
- build(rs,mid+1,rt);
- seg[nd].size=seg[ls].size+seg[rs].size-(seg[ls].r==seg[rs].l);
- seg[nd].l=seg[ls].l;
- seg[nd].r=seg[rs].r;
- }
- }
- void pushdown(int nd){
- if (seg[nd].flag!=-1){
- seg[ls].flag=seg[rs].flag=seg[nd].flag;
- seg[ls].l=seg[ls].r=seg[rs].l=seg[rs].r=seg[nd].flag;//两端都要改
- seg[ls].size=seg[rs].size=1;
- seg[nd].flag=-1;
- }
- }
- void update(int nd){
- seg[nd].size=seg[ls].size+seg[rs].size-(seg[ls].r==seg[rs].l);
- seg[nd].l=seg[ls].l,seg[nd].r=seg[rs].r;
- }
- void modify(int nd,int lf,int rt,int L,int R,int val){
- if (L>R) return;
- if (L<=lf && rt<=R){
- seg[nd].size=1;
- seg[nd].l=seg[nd].r=seg[nd].flag=val;
- return;
- }
- pushdown(nd);
- int mid=(lf+rt)>>1;
- if (L<=mid)
- modify(ls,lf,mid,L,R,val);
- if (R>mid)
- modify(rs,mid+1,rt,L,R,val);
- update(nd);
- }
- int query(int nd,int lf,int rt,int L,int R){//合并两条重链时要判重
- if (L>R) return 0;
- if (L<=lf && rt<=R)
- return seg[nd].size;
- pushdown(nd);
- int mid=(lf+rt)>>1,ans=0;
- if (L<=mid && R>mid) //合并要去重,所以有三种情况
- ans+=query(ls,lf,mid,L,R)+query(rs,mid+1,rt,L,R)-(seg[ls].r==seg[rs].l);
- else if (L<=mid)
- ans+=query(ls,lf,mid,L,R);
- else if (R>mid)
- ans+=query(rs,mid+1,rt,L,R);
- return ans;
- }
- int query_light(int nd,int lf,int rt,int v){//合并两条轻边时也要判重
- if (lf==rt)
- return seg[nd].l;
- pushdown(nd);
- int mid=(lf+rt)>>1;
- if (v<=mid)
- return query_light(ls,lf,mid,v);
- else
- return query_light(rs,mid+1,rt,v);
- }
- void modify(int u,int v,int color){
- while (top[u]!=top[v]){
- if (depth[top[u]]<depth[top[v]]) swap(u,v);
- modify(Root,1,n,in[top[u]],in[u],color);
- u=fa[top[u]];
- }
- if (depth[u]<depth[v]) swap(u,v);
- modify(Root,1,n,in[v],in[u],color);
- }
- int query(int u,int v){
- int ans=0;
- while (top[u]!=top[v]){
- if (depth[top[u]]<depth[top[v]]) swap(u,v);
- ans+=query(Root,1,n,in[top[u]],in[u]);
- //↓合并轻边的过程 ↓
- if (query_light(Root,1,n,in[top[u]])==query_light(Root,1,n,in[fa[top[u]]]))
- ans–;
- u=fa[top[u]];
- }
- if (depth[u]<depth[v]) swap(u,v);
- ans+=query(Root,1,n,in[v],in[u]);
- return ans;
- }
- int main(){
- char opt[2];
- int a,b,color;
- scanf(”%d%d”,&n,&m);
- for (int i=1;i<=n;i++)
- scanf(”%d”,&line[i]);
- for (int i=1;i<n;i++){
- scanf(”%d%d”,&a,&b);
- adde(a,b),adde(b,a);
- }
- depth[1]=1,fa[1]=1;
- dfs1(1);
- dfs2(1,1);
- build(Root,1,n);
- for (int i=1;i<=m;i++){
- scanf(”%s”,opt);
- if (opt[0]==‘C’){
- scanf(”%d%d%d”,&a,&b,&color);
- modify(a,b,color);
- }
- if (opt[0]==‘Q’){
- scanf(”%d%d”,&a,&b);
- printf(”%d\n”,query(a,b));
- }
- }
- return 0;
- }