【bzoj4817】[Sdoi2017]树点涂色

14 篇文章 0 订阅
4 篇文章 0 订阅

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的access操作,我们认为一个点到根的虚边数量+1为不同颜色种数。
这样我们就可以在access操作时修改需要修改的点。
如果一个点与其父亲的边变成虚边,则其及其子树到根的权值+1。
如果一个点与其父亲的边变成实边,则其及其子树到根的权值-1。
与子树有关的操作,我们可以用dfs序+线段树维护。
对于操作2,类似于lca求两点间的距离,d[a]+d[b]-d[lca(a,b)]+1
细节:access操作修改时,时修改splay中最左边点的子树。

代码

#include<bits/stdc++.h>
#define ll long long
#define inf 1000000000
#define mod 201314
using namespace std;
const int N=100005;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int Head[N],ret[N<<1],Next[N<<1],tot;
int l[N],r[N],tim;
int mx[N<<2],sum[N<<2],lazy[N<<2];
int dep[N],fa[N],f[N][18],a[N];
int c[N][2],up[N];
int n,m;
inline void ins(int u,int v){ret[++tot]=v;Next[tot]=Head[u];Head[u]=tot;}
void dfs(int u)
{
    l[u]=++tim;a[tim]=dep[u];
    for (int i=1;i<=17;i++) f[u][i]=f[f[u][i-1]][i-1];
    for (int i=Head[u];i;i=Next[i])
    {
        if (ret[i]==f[u][0]) continue;
        fa[ret[i]]=f[ret[i]][0]=u;
        dep[ret[i]]=dep[u]+1;
        dfs(ret[i]);
    }
    r[u]=tim;
}
inline void pushdown(int k,int l,int r)
{
    if (l==r) return;
    mx[k<<1]+=lazy[k];mx[k<<1|1]+=lazy[k];
    lazy[k<<1]+=lazy[k];lazy[k<<1|1]+=lazy[k];
    lazy[k]=0;
}
int query(int k,int l,int r,int x)
{
    if (lazy[k]) pushdown(k,l,r);
    if (l==r) return sum[k]+lazy[k];
    int mid=(l+r)>>1;
    if (x<=mid) return query(k<<1,l,mid,x);
    else return query(k<<1|1,mid+1,r,x);
}
int MX(int k,int l,int r,int x,int y)
{
    if (lazy[k]) pushdown(k,l,r);
    if (l==x&&r==y) return mx[k];
    int mid=(l+r)>>1;
    if (y<=mid) return MX(k<<1,l,mid,x,y);
    else if (x>mid) return MX(k<<1|1,mid+1,r,x,y);
    else return max(MX(k<<1,l,mid,x,mid),MX(k<<1|1,mid+1,r,mid+1,y));
}
void modify(int k,int l,int r,int x,int y,int z)
{
    if (lazy[k]) pushdown(k,l,r);
    if (l==x&&r==y){mx[k]+=z;lazy[k]+=z;return;}
    int mid=(l+r)>>1;
    if (y<=mid) modify(k<<1,l,mid,x,y,z);
    else if (x>mid) modify(k<<1|1,mid+1,r,x,y,z);
    else modify(k<<1,l,mid,x,mid,z),modify(k<<1|1,mid+1,r,mid+1,y,z);
    mx[k]=max(mx[k<<1],mx[k<<1|1]); 
}
void build(int k,int l,int r)
{
    if (l==r){mx[k]=a[l];sum[k]=a[l];return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
inline void update(int x)
{
    int l=c[x][0];
    if (l) up[x]=up[l];else up[x]=x;
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y],l,r;
    if (c[y][0]==x)l=0;else l=1;r=l^1;
    if (!isroot(y))
    {
        if (c[z][0]==y) c[z][0]=x;else c[z][1]=x;
    }
    fa[x]=z;fa[y]=x;c[y][l]=c[x][r];fa[c[x][r]]=y;c[x][r]=y;
    update(y);update(x);
}
void splay(int x)
{
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (!isroot(y))
        {
            if (c[z][0]==y^c[y][0]==x) rotate(x);else rotate(y);
        }
        rotate(x);
    }
}
void access(int x)
{
    int t=0;
    while (x)
    {
        splay(x);
        if (c[x][1]) modify(1,1,n,l[up[c[x][1]]],r[up[c[x][1]]],1);
        if (t) modify(1,1,n,l[up[t]],r[up[t]],-1);
        c[x][1]=t;
        update(x);
        t=x;
        x=fa[x];
    }
}
inline int lca(int a,int b)
{
    if (dep[a]<dep[b]) swap(a,b);
    for (int i=17;i>=0;i--)if (dep[f[a][i]]>=dep[b])a=f[a][i];
    if (a==b) return a;
    for (int i=17;i>=0;i--)if (f[a][i]!=f[b][i])
    {
        a=f[a][i];b=f[b][i];
    }
    return f[a][0];
}
int main()
{
    n=read();m=read();dep[1]=1;
    for (int i=1;i<=n;i++) up[i]=i;
    for (int i=1;i<n;i++){int u=read(),v=read();ins(u,v);ins(v,u);}
    dfs(1);build(1,1,n);
    while (m--)
    {
        int opt=read(),x=read();
        if (opt==1)access(x);
        else if (opt==2)
        {
            int y=read();
            printf("%d\n",query(1,1,n,l[y])+query(1,1,n,l[x])-2*query(1,1,n,l[lca(x,y)])+1);
        }
        else printf("%d\n",MX(1,1,n,l[x],r[x]));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值