博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

BZOJ 4817: [Sdoi2017]树点涂色

4817: [Sdoi2017]树点涂色

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 273  Solved: 164
[Submit][Status][Discuss]

Description

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x y:
在以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

Sample Output

3
4
2
2
 
感觉自己的破题能力是不是药丸啊,这么显然的LCT都没看出来……
如果我们像lct那样操作,一条路径上不同颜色数就是lct中虚边的数量+1。
那么每次多出来或者减少一条虚边的时候就修改子树权值就好了,这里可以先求出dfs序然后线段树维护。
一个细节:在splay上连边的时候,删掉的虚边并不是被作为右儿子那棵solay的根对应节点所在子树,而是这棵splay的最左一个节点。
#include<cstdio>
#include<algorithm>
#define MN 410001
#define lp (p<<1)
#define rp ((p<<1)|1)
using namespace std;

int read_p,read_ca;
inline int read(){
    read_p=0;read_ca=getchar();
    while(read_ca<'0'||read_ca>'9') read_ca=getchar();
    while(read_ca>='0'&&read_ca<='9') read_p=read_p*10+read_ca-48,read_ca=getchar();
    return read_p;
}
struct na{int y,ne;}b[MN<<1];
int n,m,o,l[MN],num=0,x,y,fa[MN],ch[MN][2],df[MN],lo[MN],nm=0,v[MN],de[MN],ma[MN],s[MN],pdf[MN],f[MN][20];
bool rt[MN];
inline int max(int a,int b){return a>b?a:b;}
inline void in(int x,int y){b[++num].y=y;b[num].ne=l[x];l[x]=num;}

void rot(int k){
    int p=fa[k],bo=ch[p][1]==k;
    ch[p][bo]=ch[k][!bo];
    ch[k][!bo]=p;
    fa[ch[p][bo]]=p;
    fa[k]=fa[p];
    fa[p]=k;
    if (rt[p]) rt[p]=0,rt[k]=1;else ch[fa[k]][ch[fa[k]][1]==p]=k;
    p=k;
}

void splay(int x){
    while (!rt[x]){
        if (rt[fa[x]]) rot(x);else
        if ((ch[fa[fa[x]]][1]==fa[x])==(ch[fa[x]][1]==x)) rot(fa[x]),rot(x);else rot(x),rot(x);
    }
}

void add(int p,int l,int r,int L,int R,int v){
    if (l==L&&r==R) s[p]+=v,ma[p]+=v;else{
        int mid=l+r>>1;
        if (R<=mid) add(lp,l,mid,L,R,v);else
        if (L>mid) add(rp,mid+1,r,L,R,v);else
        add(lp,l,mid,L,mid,v),add(rp,mid+1,r,mid+1,R,v);
        ma[p]=max(ma[lp],ma[rp])+s[p];
    }
}

int ask(int p,int l,int r,int k){
    if (l==r) return ma[p];
    int mid=l+r>>1;
    if (k<=mid) return ask(lp,l,mid,k)+s[p];else return ask(rp,mid+1,r,k)+s[p];
}

int ask(int p,int l,int r,int L,int R){
    if (L==l&&R==r) return ma[p];
    int mid=l+r>>1;
    if (R<=mid) return ask(lp,l,mid,L,R)+s[p];else
    if (L>mid) return ask(rp,mid+1,r,L,R)+s[p];else
    return max(ask(lp,l,mid,L,mid),ask(rp,mid+1,r,mid+1,R))+s[p];
}

int lf(int x){return ch[x][0]?lf(ch[x][0]):x;}

void acc(int x){
    for (int i=0;x;x=fa[i=x]){
        splay(x);
        if (ch[x][1]) add(1,1,nm,df[lf(ch[x][1])],lo[lf(ch[x][1])],1),rt[ch[x][1]]=1;
        rt[ch[x][1]=i]=0;if (ch[x][1]) add(1,1,nm,df[lf(ch[x][1])],lo[lf(ch[x][1])],-1);
    }
}

void dfs(int x){
    f[x][0]=fa[x];
    for (int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
    df[x]=++nm;rt[x]=1;de[x]=de[fa[x]]+1;pdf[nm]=de[x];
    for (int i=l[x];i;i=b[i].ne)
    if (b[i].y!=fa[x]) fa[b[i].y]=x,dfs(b[i].y);
    lo[x]=nm;
}

void build(int p,int l,int r){
    if (l==r) {ma[p]=pdf[l];return;}
    int mid=l+r>>1;
    build(lp,l,mid);build(rp,mid+1,r);
    ma[p]=max(ma[lp],ma[rp]);
}

int lca(int x,int y){
    if (de[x]<de[y]) swap(x,y);
    for (int i=19;i>=0;i--)
    if (f[x][i]&&de[f[x][i]]>=de[y]) x=f[x][i];
    if (x==y) return x;
    for (int i=19;i>=0;i--)
    if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main(){
    register int i;
    n=read();m=read();
    for (i=1;i<n;i++) x=read(),y=read(),in(x,y),in(y,x);
    dfs(1);
    build(1,1,n);
    while (m--){
        o=read();
        if (o==1) acc(read());else
        if (o==2) x=read(),y=read(),o=lca(x,y),printf("%d\n",ask(1,1,n,df[x])+ask(1,1,n,df[y])-2*ask(1,1,n,df[o])+1);else
        x=read(),printf("%d\n",ask(1,1,n,df[x],lo[x]));
    }
}
View Code

 

posted @ 2017-04-15 10:51  swm_sxt  阅读(761)  评论(0编辑  收藏  举报