[bzoj4817][动态树]树点涂色

4817: [Sdoi2017]树点涂色

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 665 Solved: 387
[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
HINT

Source

鸣谢infinityedge上传

[Submit][Status][Discuss]


sol:

注意到如果把经过一条虚边看作走了不同颜色。那么操作1就是lct的access操作。所以用线段树zici一下区间加和区间减就行了。注意到操作2的答案是f[x]+f[y]-2*f[lca]+1。这个画图证明即可。
(build线段树里面传进去了val[l]而不是val[rev[l]]的wa了这么久的应该就我一个了吧

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline int read()
{
    char c;
    bool pd=0;
    while((c=getchar())>'9'||c<'0')
    if(c=='-') pd=1;
    int res=c-'0';
    while((c=getchar())>='0'&&c<='9')
    res=(res<<3)+(res<<1)+c-'0';
    return pd?-res:res;
}
const int N=410000;
int lc[N],rc[N],fa[N];
int tot,fir[N],go[N],nex[N];
int acs[N],tag[N],size[N],dfn[N],tim,rev[N];
inline void updata(int k)
{
    acs[k]=max(acs[k<<1],acs[k<<1|1]);
}
inline void tag_down(int k)
{
    if(tag[k])
    {
        tag[k<<1]+=tag[k];
        tag[k<<1|1]+=tag[k];
        acs[k<<1]+=tag[k];
        acs[k<<1|1]+=tag[k];
        tag[k]=0;
    }
}
inline void query(int k,int l,int r,int L,int R,int &ans)
{
    if(L<=l&&r<=R)
    {
        if(acs[k]>ans) ans=acs[k];
        return;
    }
    tag_down(k);
    int mid=l+r>>1;
    if(mid>=L) query(k<<1,l,mid,L,R,ans);
    if(mid< R) query(k<<1|1,mid+1,r,L,R,ans);
    updata(k);
}
inline void modify(int k,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        tag[k]+=v;
        acs[k]+=v;
        return;
    }
    tag_down(k);
    int mid=l+r>>1;
    if(mid>=L) modify(k<<1,l,mid,L,R,v);
    if(mid< R) modify(k<<1|1,mid+1,r,L,R,v);
    updata(k);
}
int n;
inline void modify(int x,int v)
{
    if(!x) return;
    while(lc[x]) x=lc[x];
    modify(1,1,n,dfn[x],dfn[x]+size[x]-1,v);
}
inline void add(int x,int y)
{
    nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
    nex[++tot]=fir[y];fir[y]=tot;go[tot]=x;
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=lc[y]==x?rc[x]:lc[x];
    if(b) fa[b]=y;
    fa[x]=z;fa[y]=x;
    if(z)
    {
        if(lc[z]==y) lc[z]=x;
        if(rc[z]==y) rc[z]=x;
    }
    if(lc[y]==x) lc[y]=b,rc[x]=y;
    else rc[y]=b,lc[x]=y;
}
inline bool is_root(int x)
{
    return lc[fa[x]]!=x&&rc[fa[x]]!=x;
}
inline void splay(int x)
{
    while(!is_root(x))
    {
        if(!is_root(fa[x]))
        {
            if((lc[fa[fa[x]]]==fa[x])==(lc[fa[x]]==x)) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
}
inline void access(int q)
{
    for(int p=0;q;p=q,q=fa[q])
    {
        splay(q);
        modify(rc[q],1);
        rc[q]=p;
        modify(p,-1);
    }
}int dep[N];
const int maxlog=16;
int top[N][maxlog+1];
inline int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int j=maxlog;~j;--j)
    if(dep[x]-(1<<j)>=dep[y]) x=top[x][j];
    if(x==y) return x;
    for(int j=maxlog;~j;--j)
    if(top[x][j]!=top[y][j])
    {
        x=top[x][j];
        y=top[y][j];
    }
    return top[x][0];
}
inline void dfs(int u,int f)
{
    dfn[u]=++tim;
    size[u]++;
    top[u][0]=f;
    int e,v;
    dep[u]=dep[f]+1;
    rev[dfn[u]]=dep[u];
    for(e=fir[u];v=go[e],e;e=nex[e])
    if(v!=f)
    {
        fa[v]=u;
        dfs(v,u);
        size[u]+=size[v];
    }
}int ans,tmp,Lca;
inline void build(int k,int l,int r)
{
    if(l==r)
    {
        acs[k]=rev[l];
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    updata(k);
}int m;
inline int find_root(int x)
{
    splay(x);
    while(lc[x]) x=lc[x];
    return x;
}
int main()
{
//  freopen("4817.in","r",stdin);
//  freopen("4817.out","w",stdout);
    n=read();
    m=read();
    for(int i=2;i<=n;++i) add(read(),read());
    dfs(1,0);
    build(1,1,n);
    for(int j=1;j<=maxlog;++j)
    for(int i=1;i<=n;++i)
    top[i][j]=top[top[i][j-1]][j-1];
    for(int i=1;i<=m;++i)
    {
        int tp,x,y;
        tp=read();
        x=read();
        if(tp==1)
        access(x);
        if(tp==2)
        {
            y=read();
            Lca=lca(x,y);
            ans=0;
            query(1,1,n,dfn[y],dfn[y],tmp=0);
            ans+=tmp;
            query(1,1,n,dfn[x],dfn[x],tmp=0);
            ans+=tmp;
            query(1,1,n,dfn[Lca],dfn[Lca],tmp=0);
            ans-=tmp*2;
            printf("%d\n",ans+1);
        }
        if(tp==3)
        {
            ans=0;
            query(1,1,n,dfn[x],dfn[x]+size[x]-1,ans);
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值