BZOJ4817: [Sdoi2017]树点涂色
Description
Bob有一棵n个点的有根树,其中1号点是根节点。
Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x:
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作
Input
第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000
Output
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
Sample Input
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
Sample Output
3
4
2
2
4
2
2
题解Here!
看到题目第一想法:树剖。
但是1操作要改一颗子树,还要分 在链上 与 不在链上 两种,可能还有树上分治什么乱七八糟的玩意,时间+空间+思考+代码 复杂度均不能接受。。。
正解:Link-Cut-Tree。。。
先构出原树的虚树,并用树剖维护每个点到根节点的路径权值data,那么每个点到根节点的路径权值 data 就是每个点到根节点的路径上 实链 的个数。
于是1操作就变成了access:
如果一条实边变成虚边,那么将连接这条边的深度较大的节点的子树里所有点的 data 加 1 (因为实链数量就等于虚边数量+1 );
如果一条虚边变成实边,那么将连接这条边的深度较大的节点的子树里所有点的 data 减 1 。
因为没有加删边,故2、3操作还是用 树剖+线段树 维护。
2操作:
ans=data[x]+data[y]-data[LCA(x,y)]*2+1;
3操作:
ans=query_max(id[x],id[x]+size[x]-1);
代码中为了防止 线段树 与 LCT 混淆,用了 namespace 封装。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 using namespace std; int n,m,c=1,d=1; int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN]; struct node{ int next,to; }a[MAXN<<1]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } namespace ST{ #define LSON rt<<1 #define RSON rt<<1|1 #define DATA(x) a[x].data #define SIGN(x) a[x].c #define LSIDE(x) a[x].l #define RSIDE(x) a[x].r struct Segment_Tree{ int data,c,l,r; }a[MAXN<<2]; inline void pushup(int rt){ DATA(rt)=max(DATA(LSON),DATA(RSON)); } inline void pushdown(int rt){ if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return; SIGN(LSON)+=SIGN(rt);DATA(LSON)+=SIGN(rt); SIGN(RSON)+=SIGN(rt);DATA(RSON)+=SIGN(rt); SIGN(rt)=0; } void buildtree(int l,int r,int rt){ int mid; LSIDE(rt)=l; RSIDE(rt)=r; if(l==r){ DATA(rt)=deep[pos[l]]; return; } mid=l+r>>1; buildtree(l,mid,LSON); buildtree(mid+1,r,RSON); pushup(rt); } void update(int l,int r,int c,int rt){ int mid; if(l<=LSIDE(rt)&&RSIDE(rt)<=r){ SIGN(rt)+=c;DATA(rt)+=c; return; } pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)update(l,r,c,LSON); if(mid<r)update(l,r,c,RSON); pushup(rt); } int query(int l,int r,int rt){ int mid,ans=0; if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt); pushdown(rt); mid=LSIDE(rt)+RSIDE(rt)>>1; if(l<=mid)ans=max(ans,query(l,r,LSON)); if(mid<r)ans=max(ans,query(l,r,RSON)); return ans; } } namespace LCT{ int top,stack[MAXN]; struct Link_Cut_Tree{ int f,flag,son[2]; int v; }a[MAXN]; inline bool isroot(int rt){ return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt; } inline void pushup(int rt){ if(!rt)return; if(a[rt].son[0])a[rt].v=a[a[rt].son[0]].v; else a[rt].v=rt; } inline void pushdown(int rt){ if(!rt||!a[rt].flag)return; a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1; swap(a[rt].son[0],a[rt].son[1]); } inline void turn(int rt){ int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0; if(!isroot(x)){ if(a[y].son[0]==x)a[y].son[0]=rt; else a[y].son[1]=rt; } a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x; a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x; pushup(x);pushup(rt); } void splay(int rt){ top=0; stack[++top]=rt; for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f; while(top)pushdown(stack[top--]); while(!isroot(rt)){ int x=a[rt].f,y=a[x].f; if(!isroot(x)){ if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt); else turn(x); } turn(rt); } } void access(int rt){ for(int i=0;rt;i=rt,rt=a[rt].f){ splay(rt); if(a[rt].son[1])ST::update(id[a[a[rt].son[1]].v],id[a[a[rt].son[1]].v]+size[a[a[rt].son[1]].v]-1,1,1); if(i)ST::update(id[a[i].v],id[a[i].v]+size[a[i].v]-1,-1,1); a[rt].son[1]=i; if(i)a[i].f=rt; } } } inline void add(int x,int y){ a[c].to=y;a[c].next=head[x];head[x]=c++; a[c].to=x;a[c].next=head[y];head[y]=c++; } void dfs1(int rt){ son[rt]=0;size[rt]=1; for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(!deep[will]){ deep[will]=deep[rt]+1; fa[will]=rt; dfs1(will); size[rt]+=size[will]; if(size[son[rt]]<size[will])son[rt]=will; } } } void dfs2(int rt,int f){ id[rt]=d++;pos[d-1]=rt;top[rt]=f; if(son[rt])dfs2(son[rt],f); for(int i=head[rt];i;i=a[i].next){ int will=a[i].to; if(will!=fa[rt]&&will!=son[rt]) dfs2(will,will); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } if(deep[x]>deep[y])swap(x,y); return x; } void work(){ int f,x,y; while(m--){ f=read();x=read(); if(f==1)LCT::access(x); if(f==2){ y=read(); int fa=LCA(x,y); printf("%d\n",(ST::query(id[x],id[x],1)+ST::query(id[y],id[y],1)-ST::query(id[fa],id[fa],1)*2+1)); } if(f==3)printf("%d\n",ST::query(id[x],id[x]+size[x]-1,1)); } } void init(){ int x,y; n=read();m=read(); for(int i=1;i<n;i++){ x=read();y=read(); add(x,y); } deep[1]=1; dfs1(1); dfs2(1,1); ST::buildtree(1,n,1); for(int i=1;i<=n;i++){ LCT::a[i].f=fa[i]; LCT::a[i].v=i; } } int main(){ init(); work(); return 0; }