BZOJ4817[Sdoi2017]树点涂色——LCT+线段树
题目描述
Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作
输入
第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000
输出
每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值
样例输入
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
样例输出
3
4
2
2
4
2
2
我们将连续相同颜色的一条链看成一条重链,可以发现修改操作其实就是$LCT$里的$access$操作。
那么每个节点到根的权值就是这个点到根路径上的重链数。
考虑用线段树维护出每个点到根的权值,每次修改时假设路径上$x->fa$的边是轻边,$fa$的重儿子是$son$,那么就将$son$子树中所有点的答案$+1$,$x$子树中所有点答案$-1$,然后将$x$变为$fa$的重儿子。在$access$时完成子树修改操作即可。
对于查询$x$到$y$的路径权值,就是$v[x]+v[y]-2v[lca]+1$。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<bitset> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; int c[100010][2]; int fa[100010]; int tag[400010]; int f[100010][18]; int dep[100010]; int mx[400010]; int dfn; int tot; int x,y; int n,m; int opt; int head[100010]; int next[200010]; int to[200010]; int q[100010]; int s[100010]; int t[100010]; void add(int x,int y) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; } void pushup(int rt) { mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); } void pushdown(int rt) { if(tag[rt]) { tag[rt<<1]+=tag[rt]; tag[rt<<1|1]+=tag[rt]; mx[rt<<1]+=tag[rt]; mx[rt<<1|1]+=tag[rt]; tag[rt]=0; } } void build(int rt,int l,int r) { if(l==r) { mx[rt]=dep[q[l]]; return ; } int mid=(l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); pushup(rt); } void change(int rt,int l,int r,int L,int R,int v) { if(L<=l&&r<=R) { tag[rt]+=v; mx[rt]+=v; return ; } pushdown(rt); int mid=(l+r)>>1; if(L<=mid) { change(rt<<1,l,mid,L,R,v); } if(R>mid) { change(rt<<1|1,mid+1,r,L,R,v); } pushup(rt); } int query(int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) { return mx[rt]; } pushdown(rt); int mid=(l+r)>>1; int res=0; if(L<=mid) { res=max(res,query(rt<<1,l,mid,L,R)); } if(R>mid) { res=max(res,query(rt<<1|1,mid+1,r,L,R)); } return res; } int ask(int rt,int l,int r,int k) { if(l==r) { return mx[rt]; } pushdown(rt); int mid=(l+r)>>1; if(k<=mid) { return ask(rt<<1,l,mid,k); } else { return ask(rt<<1|1,mid+1,r,k); } } bool is_root(int rt) { return rt!=c[fa[rt]][0]&&rt!=c[fa[rt]][1]; } bool get(int rt) { return rt==c[fa[rt]][1]; } void rotate(int rt) { int x=fa[rt]; int y=fa[x]; int k=get(rt); if(!is_root(x)) { c[y][get(x)]=rt; } c[x][k]=c[rt][k^1]; fa[c[rt][k^1]]=x; c[rt][k^1]=x; fa[x]=rt; fa[rt]=y; } void splay(int rt) { for(int x;!is_root(rt);rotate(rt)) { if(!is_root(x=fa[rt])) { rotate(get(x)==get(rt)?x:rt); } } } void link(int x,int y) { fa[x]=y; } int find(int rt) { while(c[rt][0]) { rt=c[rt][0]; } return rt; } void access(int rt) { for(int x=0;rt;x=rt,rt=fa[rt]) { splay(rt); if(x) { int son=find(x); change(1,1,n,s[son],t[son],-1); } if(c[rt][1]) { int son=find(c[rt][1]); change(1,1,n,s[son],t[son],1); } c[rt][1]=x; } } void dfs(int x) { s[x]=++dfn; q[dfn]=x; for(int i=1;i<=17;i++) { f[x][i]=f[f[x][i-1]][i-1]; } for(int i=head[x];i;i=next[i]) { if(to[i]!=f[x][0]) { f[to[i]][0]=x; link(to[i],x); dep[to[i]]=dep[x]+1; dfs(to[i]); } } t[x]=dfn; } int lca(int x,int y) { if(dep[x]<dep[y]) { swap(x,y); } int d=dep[x]-dep[y]; for(int i=0;i<=17;i++) { if(d&(1<<i)) { x=f[x][i]; } } if(x==y) { return x; } for(int i=17;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dep[1]=1; dfs(1); build(1,1,n); while(m--) { scanf("%d",&opt); if(opt==1) { scanf("%d",&x); access(x); } else if(opt==2) { scanf("%d%d",&x,&y); int anc=lca(x,y); int ans=ask(1,1,n,s[x])+ask(1,1,n,s[y])-2*ask(1,1,n,s[anc])+1; printf("%d\n",ans); } else { scanf("%d",&x); printf("%d\n",query(1,1,n,s[x],t[x])); } } }