【bzoj4817】[Sdoi2017]树点涂色 【树链剖分】【LCT】【线段树】
blog终于update了= =
题目链接
题意:给一棵树,支持三种操作
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
题解:
通过观察,我们发现同一种颜色一定连在一起的,因此第2个询问就相当于求链上颜色段的数目,直接使用树剖套线段树维护一下。
第一个修改操作其实和LCT的access很像,把到根路径上的所有点都丢到一起。所以我们可以对于1操作直接access,这样每个节点的到根路径的的权值就是虚边的数量+1。我们就在access的时候,虚边变化时维护一下就好。注意每一棵LCT中的小splay实际的根是最靠左的节点,就是中序遍历最靠前的节点。
其实不需要分两种方法的= = 第二种询问直接在LCT上维护就行了。
代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=100005;
int n,m,op,u,v;
namespace tree{
int cnt,tot,head[N],to[N*2],nxt[N*2];
int idx,fa[N],dep[N],siz[N],son[N],top[N],dfn[N],pos[N];
struct data{
int lc,rc,sc;
bool flag;
data(){
flag=false;
}
}col[N*4];
int tag[N*4];
data merge(data a,data b){
if(!a.flag){
return b;
}else if(!b.flag){
return a;
}
data c;
c.flag=true;
c.sc=a.sc+b.sc;
if(a.rc==b.lc){
c.sc--;
}
c.lc=a.lc;
c.rc=b.rc;
return c;
}
void adde(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u){
int v;
siz[u]=1;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
}
void dfs(int u,int tp){
top[u]=tp;
dfn[u]=++idx;
pos[idx]=u;
if(son[u]){
dfs(son[u],tp);
}
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]&&v!=son[u]){
dfs(v,v);
}
}
}
void build(int o,int l,int r){
if(l==r){
col[o].sc=1;
col[o].lc=col[o].rc=pos[l];
col[o].flag=true;
return;
}
int mid=(l+r)/2;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
col[o]=merge(col[o*2],col[o*2+1]);
}
void pushdown(int o){
if(tag[o]){
tag[o*2]=tag[o*2+1]=col[o*2].lc=col[o*2].rc=col[o*2+1].lc=col[o*2+1].rc=tag[o];
col[o*2].sc=col[o*2+1].sc=1;
tag[o]=0;
}
}
void update(int o,int l,int r,int L,int R){
if(L==l&&R==r){
tag[o]=col[o].lc=col[o].rc=tot;
col[o].sc=1;
col[o].flag=true;
return;
}
pushdown(o);
int mid=(l+r)/2;
if(R<=mid){
update(o*2,l,mid,L,R);
}else if(L>mid){
update(o*2+1,mid+1,r,L,R);
}else{
update(o*2,l,mid,L,mid);
update(o*2+1,mid+1,r,mid+1,R);
}
col[o]=merge(col[o*2],col[o*2+1]);
}
data query(int o,int l,int r,int L,int R){
if(L==l&&R==r){
return col[o];
}
pushdown(o);
int mid=(l+r)/2;
if(R<=mid){
return query(o*2,l,mid,L,R);
}else if(L>mid){
return query(o*2+1,mid+1,r,L,R);
}else{
return merge(query(o*2,l,mid,L,mid),query(o*2+1,mid+1,r,mid+1,R));
}
}
void modify(int u){
tot++;
while(top[u]!=1){
update(1,1,n,dfn[top[u]],dfn[u]);
u=fa[top[u]];
}
update(1,1,n,1,dfn[u]);
}
int solve(int x,int y){
data ansx,ansy,res;
bool flagx=false,flagy=false;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]){
res=query(1,1,n,dfn[top[x]],dfn[x]);
if(!flagx){
flagx=true;
ansx=res;
}else{
ansx=merge(ansx,res);
}
x=fa[top[x]];
}else{
res=query(1,1,n,dfn[top[y]],dfn[y]);
if(!flagy){
flagy=true;
ansy=res;
}else{
ansy=merge(res,ansy);
}
y=fa[top[y]];
}
}
if(dep[x]<dep[y]){
res=query(1,1,n,dfn[x],dfn[y]);
if(!flagy){
flagy=true;
ansy=res;
}else{
ansy=merge(res,ansy);
}
}else{
res=query(1,1,n,dfn[y],dfn[x]);
if(!flagx){
flagx=true;
ansx=res;
}else{
ansx=merge(ansx,res);
}
}
if(!flagy){
return ansx.sc;
}else if(!flagx){
return ansy.sc;
}else{
return merge(ansx,ansy).sc;
}
}
}
namespace sgt{
int maxn[N*4],tag[N*4];
void pushdown(int o){
if(tag[o]){
tag[o*2]+=tag[o];
tag[o*2+1]+=tag[o];
maxn[o*2]+=tag[o];
maxn[o*2+1]+=tag[o];
tag[o]=0;
}
}
void update(int o,int l,int r,int L,int R,int v){
if(L==l&&R==r){
maxn[o]+=v;
tag[o]+=v;
return;
}
pushdown(o);
int mid=(l+r)/2;
if(R<=mid){
update(o*2,l,mid,L,R,v);
}else if(L>mid){
update(o*2+1,mid+1,r,L,R,v);
}else{
update(o*2,l,mid,L,mid,v);
update(o*2+1,mid+1,r,mid+1,R,v);
}
maxn[o]=max(maxn[o*2],maxn[o*2+1]);
}
int query(int o,int l,int r,int L,int R){
if(L==l&&R==r){
return maxn[o];
}
pushdown(o);
int mid=(l+r)/2;
if(R<=mid){
return query(o*2,l,mid,L,R);
}else if(L>mid){
return query(o*2+1,mid+1,r,L,R);
}else{
return max(query(o*2,l,mid,L,mid),query(o*2+1,mid+1,r,mid+1,R));
}
}
}
namespace lct{
int fa[N],ch[N][2];
void build(int u){
fa[u]=tree::fa[u];
int v;
for(int i=tree::head[u];i;i=tree::nxt[i]){
v=tree::to[i];
if(v!=tree::fa[u]){
build(v);
}
}
}
bool isroot(int u){
return u!=ch[fa[u]][0]&&u!=ch[fa[u]][1];
}
int which(int u){
return u==ch[fa[u]][1];
}
void rotate(int x){
int y=fa[x],md=which(x);
if(!isroot(y)){
ch[fa[y]][which(y)]=x;
}
fa[x]=fa[y];
ch[y][md]=ch[x][!md];
fa[ch[y][md]]=y;
ch[x][!md]=y;
fa[y]=x;
}
void splay(int u){
while(!isroot(u)){
if(!isroot(fa[u])){
rotate(which(u)==which(fa[u])?fa[u]:u);
}
rotate(u);
}
}
int getroot(int u){
while(ch[u][0]){
u=ch[u][0];
}
return u;
}
void access(int u){
for(int v=0,res;u;v=u,u=fa[u]){
splay(u);
if(ch[u][1]){
res=getroot(ch[u][1]);
sgt::update(1,1,n,tree::dfn[res],tree::dfn[res]+tree::siz[res]-1,1);
}
ch[u][1]=v;
if(ch[u][1]){
res=getroot(ch[u][1]);
sgt::update(1,1,n,tree::dfn[res],tree::dfn[res]+tree::siz[res]-1,-1);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
tree::adde(u,v);
tree::adde(v,u);
}
tree::dfs(1);
tree::dfs(1,1);
tree::build(1,1,n);
for(int i=1;i<=n;i++){
sgt::update(1,1,n,i,i,tree::dep[tree::pos[i]]+1);
}
tree::tot=n;
lct::build(1);
while(m--){
scanf("%d%d",&op,&u);
if(op==1){
tree::modify(u);
lct::access(u);
}else if(op==2){
scanf("%d",&v);
printf("%d\n",tree::solve(u,v));
}else{
printf("%d\n",sgt::query(1,1,n,tree::dfn[u],tree::dfn[u]+tree::siz[u]-1));
}
}
return 0;
}