P2486 [SDOI2011]染色
先考虑在一段序列上进行操作如何维护
线段树
记录每个区间的的颜色段数量
但是区间合并时两边可能颜色相同
所以再记录一下每个区间最左和最右的颜色
合并时如果相邻两边颜色相同,那么颜色段数量就要减一
然后考虑在树上操作
直接上树剖,一样用线段树维护就好了
询问时记录一下当前左右两边最上面的颜色
然后跟取出的区间最下面的颜色比较一下
如果颜色相同答案也要减1
具体细节在代码里
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=4e5+7; int n,m; int c[N];//原树上颜色 int fir[N<<2],from[N<<2],to[N<<2],cntt;//前向星存树 inline void add(int &a,int &b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } int fa[N],sz[N],dep[N],son[N];//以下为树剖的预处理,不解释 void dfs1(int &x,int &f) { dep[x]=dep[f]+1; fa[x]=f; sz[x]=1; int mxsz=0; for(int i=fir[x];i;i=from[i]) { if(to[i]==f) continue; dfs1(to[i],x); sz[x]+=sz[to[i]]; if(sz[to[i]]>mxsz) { mxsz=sz[to[i]]; son[x]=to[i]; } } } int id[N],col[N]/*col存线段树上的颜色*/,Top[N],cnt; void dfs2(int &x,int &topp) { id[x]=++cnt; col[cnt]=c[x]; Top[x]=topp; if(!son[x]) return; dfs2(son[x],topp); for(int i=fir[x];i;i=from[i]) { if(to[i]==fa[x]||to[i]==son[x]) continue; dfs2(to[i],to[i]); } }//以上为树剖预处理 //---------------------------------------------------------------- int t[N<<2],l_col[N<<2],r_col[N<<2],laz[N<<2];//t存颜色段数量,l_col和r_col分别存左右端点的颜色,laz为懒标记 inline void pushdown(int &o)//下传laz { if(laz[o]) { t[o<<1]=t[o<<1|1]=1; laz[o<<1]=laz[o<<1|1]=l_col[o<<1]=r_col[o<<1]=l_col[o<<1|1]=r_col[o<<1|1]=laz[o]; laz[o]=0; } } inline void pushup(int &o)//用儿子节点更新当前节点 { t[o]=t[o<<1]+t[o<<1|1] - (r_col[o<<1]==l_col[o<<1|1] ? 1 : 0); l_col[o]=l_col[o<<1]; r_col[o]=r_col[o<<1|1]; } void build(int o,int l,int r)//建树 { if(l==r) { t[o]=1; l_col[o]=r_col[o]=col[l]; return; } int mid=l+r>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); pushup(o); } void change(int o,int l,int r,int ql,int qr,int Col)//改变颜色 { if(l>=ql&&r<=qr) { t[o]=1; l_col[o]=r_col[o]=laz[o]=Col; return; } pushdown(o); int mid=l+r>>1; if(ql<=mid) change(o<<1,l,mid,ql,qr,Col); if(qr>mid) change(o<<1|1,mid+1,r,ql,qr,Col); pushup(o); } int Lc=0,Rc=0;//记录最左和最右的颜色 int query(int o,int l,int r,int ql,int qr)//询问一个区间有多少颜色段 { if(l>=ql&&r<=qr) { if(l==ql) Lc=l_col[o];//记录左右端点颜色 if(r==qr) Rc=r_col[o]; return t[o]; } pushdown(o); int mid=l+r>>1,res=0; if(ql<=mid) res+=query(o<<1,l,mid,ql,qr); if(qr>mid) res+=query(o<<1|1,mid+1,r,ql,qr); if(ql<=mid&&qr>mid) res-=(r_col[o<<1]==l_col[o<<1|1] ? 1 : 0);//注意判断颜色相同 pushup(o); return res; } //---------------------------------------------------------------- void Color(int x,int y,int &z)//改变两点间的颜色 { while(Top[x]!=Top[y]) { if(dep[Top[x]]<dep[Top[y]]) swap(x,y); change(1,1,n,id[Top[x]],id[x],z); x=fa[Top[x]]; } if(dep[x]<dep[y]) swap(x,y); change(1,1,n,id[y],id[x],z); } void Query(int x,int y)//询问两点间的颜色段数量 { int res=0,xc=0,yc=0;//xc,yc存树上两边当前最上面的颜色 while(Top[x]!=Top[y]) { if(dep[Top[x]]<dep[Top[y]]) swap(x,y),swap(xc,yc);//注意xc,yc也要交换 res+=query(1,1,n,id[Top[x]],id[x]); if( Rc==xc ) res--;//如果取出的区间最右(即最下)边与当前最上面的颜色一样,就要减1 x=fa[Top[x]]; xc=Lc; } if(dep[x]<dep[y]) swap(x,y),swap(xc,yc); res+=query(1,1,n,id[y],id[x]); if(Rc==xc) res--; if(Lc==yc) res--;//注意最后一步要与两边都判一下 printf("%d\n",res); } int main() { int a,b; n=read(); m=read(); for(int i=1;i<=n;i++) c[i]=read(); for(int i=1;i<n;i++) a=read(),b=read(),add(a,b),add(b,a); int rt=1; dfs1(rt,rt); dfs2(rt,rt); build(1,1,n); char ch[5]; int x; for(int i=1;i<=m;i++) { scanf("%s",ch); a=read(); b=read(); if(ch[0]=='C') { x=read(); Color(a,b,x); } else Query(a,b); } return 0; }