[SDOI2011]染色(树链剖分)
Posted hsez-cyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2011]染色(树链剖分)相关的知识,希望对你有一定的参考价值。
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)路径上的颜色段数量。
ouput
对于每个询问操作,输出一行答案。
Solution
树链剖分处理色块(注意相邻两段的首尾颜色相同的情况)
主要是细节,详见代码
Code
#include <cstdio> #include <cstdlib> #include <vector> using namespace std; const int N=1e5+10; struct node { int l,r,lc,rc,cl,cr,sum; bool flag; }f[N*2]; int fa[N],deep[N],son[N],d[N],dfn[N],si[N],top[N],rt, tot,cnt,n,m,u,v,c,re[N],L,R,a,b; char s[5]; vector <int> link[N]; void dfs1(int u,int f) { fa[u]=f; deep[u]=deep[f]+1; si[u]=1; int size=link[u].size(); for(int i=0;i<size;i++) { int v=link[u][i]; if(v!=f) { dfs1(v,u); si[u]+=si[v]; if(son[u]==0 || si[son[u]]<si[v]) son[u]=v; } } } void dfs2(int u,int f) { dfn[u]=++cnt,re[cnt]=u; if(son[u]!=0) top[son[u]]=top[u],dfs2(son[u],u); int size=link[u].size(); for(int i=0;i<size;i++) { int v=link[u][i]; if(v!=f && v!=son[u]) top[v]=v,dfs2(v,u); } } void push_up(int g) { int lc=f[g].lc,rc=f[g].rc; f[g].cl=f[lc].cl,f[g].cr=f[rc].cr; f[g].sum=f[lc].sum+f[rc].sum; if(f[lc].cr==f[rc].cl) f[g].sum--; } void push_down(int g) { if(f[g].flag) { int lc=f[g].lc,rc=f[g].rc; f[lc].flag=f[rc].flag=true; f[lc].cl=f[lc].cr=f[rc].cl=f[rc].cr=f[g].cl; f[lc].sum=f[rc].sum=1; f[g].flag=false; } } void build(int &g,int l,int r) { g=++tot; f[g].l=l,f[g].r=r; if(l==r) { f[g].flag=true,f[g].sum=1; f[g].cl=f[g].cr=d[re[l]]; return ; } int mid=(l+r)>>1; build(f[g].lc,l,mid); build(f[g].rc,mid+1,r); push_up(g); } void add(int g,int l,int r,int c) { if(f[g].l>=l && f[g].r<=r) f[g].flag=true,f[g].cl=f[g].cr=c,f[g].sum=1; else { push_down(g); int mid=(f[g].l+f[g].r)>>1; if(r<=mid) add(f[g].lc,l,r,c); else if(l>mid) add(f[g].rc,l,r,c); else add(f[g].lc,l,mid,c),add(f[g].rc,mid+1,r,c); push_up(g); } } void Add(int x,int y,int c) { int px=top[x],py=top[y]; while(px!=py) if(deep[px]>deep[py]) add(rt,dfn[px],dfn[x],c),x=fa[px],px=top[x]; else add(rt,dfn[py],dfn[y],c),y=fa[py],py=top[y]; if(dfn[x]<dfn[y]) add(rt,dfn[x],dfn[y],c); else add(rt,dfn[y],dfn[x],c); } int get(int g,int l,int r) { if(f[g].l==L) a=f[g].cl; if(f[g].r==R) b=f[g].cr; if(f[g].l>=l && f[g].r<=r) return f[g].sum; else { push_down(g); int mid=(f[g].l+f[g].r)>>1; if(r<=mid) return get(f[g].lc,l,r); else if(l>mid) return get(f[g].rc,l,r); else { int ans=get(f[g].lc,l,mid)+get(f[g].rc,mid+1,r); if(f[f[g].lc].cr==f[f[g].rc].cl) ans--; return ans; } } } int Get(int x,int y) { int ans=0; int px=top[x],py=top[y]; int l_x=-1,l_y=-1; while(px!=py) if(deep[px]>deep[py]) { L=dfn[px],R=dfn[x]; ans+=get(rt,dfn[px],dfn[x]); x=fa[px],px=top[x]; if(b==l_x) ans--; l_x=a; } else { L=dfn[py],R=dfn[y]; ans+=get(rt,dfn[py],dfn[y]); y=fa[py],py=top[y]; if(b==l_y) ans--; l_y=a; } if(dfn[x]>dfn[y]) swap(x,y),swap(l_x,l_y); L=dfn[x],R=dfn[y]; ans+=get(rt,dfn[x],dfn[y]); if(a==l_x) ans--; if(b==l_y) ans--; return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&d[i]); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); link[u].push_back(v); link[v].push_back(u); } dfs1(1,0); top[1]=1,dfs2(1,0); build(rt,1,cnt); while(m--) { scanf("%s%d%d",s,&u,&v); if(s[0]==‘C‘) { scanf("%d",&c); Add(u,v,c); } else if(s[0]==‘Q‘) printf("%d ",Get(u,v)); } return 0; }
以上是关于[SDOI2011]染色(树链剖分)的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 2243 2243: [SDOI2011]染色 树链剖分