洛谷P3703 [SDOI2017]树点涂色

题目描述

Bob有一棵nn 个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。

定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。

Bob可能会进行这几种操作:

  • 1 x

把点xx 到根节点的路径上所有的点染上一种没有用过的新颜色。

  • 2 x y

xx 到yy 的路径的权值。

  • 3 x

在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

Bob一共会进行mm 次操作

输入输出格式

输入格式:

第一行两个数n,mn,m 。

接下来n-1n1 行,每行两个数a,ba,b ,表示aa 与bb 之间有一条边。

接下来mm 行,表示操作,格式见题目描述

输出格式:

每当出现2,3操作,输出一行。

如果是2操作,输出一个数表示路径的权值

如果是3操作,输出一个数表示权值的最大值

输入输出样例

输入样例#1: 
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: 
3
4
2
2

说明

共10个测试点

测试点1,1\leq n,m\leq10001n,m1000

测试点2、3,没有2操作

测试点4、5,没有3操作

测试点6,树的生成方式是,对于i(2\leq i \leq n)i(2in) ,在1到i-1i1 中随机选一个点作为i的父节点。

测试点7,1\leq n,m\leq 500001n,m50000

测试点8,1\leq n \leq 500001n50000

测试点9,10,无特殊限制

对所有数据,1\leq n \leq 10^51n105 ,1\leq m \leq 10^51m105

时间限制:1s

空间限制:128MB

LCT+树链剖分+线段树。。。

首先,按照原树,构建出一个全部都是虚边的LCTLCT ,并用树剖维护每个点到根节点的路径权值valval 。可以发现,每个点到根节点的路径权值就是每个点到根节点的路径上实链的个数。

我们发现,操作11 实际上就是LCTLCT 中AccessAccess 的操作。在AccessAccess 的操作中,如果一条实边变成虚边,那么将连接这条边的深度较大的节点的子树里所有点的valval 加 11 (因为实链数量就等于虚边数量+1+1 ),如果一条虚边变成实边,那么将连接这条边的深度较大的节点的子树里所有点的valval 减11 。

这样,询问22 其实就是求val[x]+val[y]-val[lca(x,y)]*2+1val[x]+val[y]val[lca(x,y)]2+1 的值,询问33 就是求xx 的子树最大值。

为了好区分 LCT 与 线段树,直接 namespace 封装。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 100010
using namespace std;
int n,m,c=1,d=1;
int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
int stack[MAXN];
struct node1{
    int next,to;
}a[MAXN<<1];
namespace ST{
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define DATA(x) a[x].data
    #define SIGN(x) a[x].c
    #define LSIDE(x) a[x].l
    #define RSIDE(x) a[x].r
    struct Sg_Tree{
        int data,c,l,r;
    }a[MAXN<<2];
    inline void pushup(int rt){
        DATA(rt)=max(DATA(LSON),DATA(RSON));
    }
    inline void pushdown(int rt){
        if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
        SIGN(LSON)+=SIGN(rt);DATA(LSON)+=SIGN(rt);
        SIGN(RSON)+=SIGN(rt);DATA(RSON)+=SIGN(rt);
        SIGN(rt)=0;
    }
    void buildtree(int l,int r,int rt){
        int mid;
        LSIDE(rt)=l;
        RSIDE(rt)=r;
        if(l==r){
            DATA(rt)=0;
            return;
        }
        mid=l+r>>1;
        buildtree(l,mid,LSON);
        buildtree(mid+1,r,RSON);
        pushup(rt);
    }
    void update(int l,int r,int c,int rt){
        int mid;
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
            SIGN(rt)+=c;DATA(rt)+=c;
            return;
        }
        pushdown(rt);
        mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)update(l,r,c,LSON);
        if(mid<r)update(l,r,c,RSON);
        pushup(rt);
    }
    int query(int l,int r,int rt){
        int mid,ans=0;
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
        pushdown(rt);
        mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)ans=max(ans,query(l,r,LSON));
        if(mid<r)ans=max(ans,query(l,r,RSON));
        return ans;
    }
}
namespace LCT{
    struct Link_Cut_Tree{
        int son[2];
        int f,s,flag;
    }a[MAXN];
    inline bool isroot(int rt){
        return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
    }
    inline void pushup(int rt){
        if(!rt)return;
        if(a[rt].son[0])a[rt].s=a[a[rt].son[0]].s;
        else a[rt].s=rt;
    }
    inline void pushdown(int rt){
        if(!rt||!a[rt].flag)return;
        a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
        swap(a[rt].son[0],a[rt].son[1]);
    }
    inline void turn(int rt){
        int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
        if(!isroot(x)){
            if(a[y].son[0]==x)a[y].son[0]=rt;
            else a[y].son[1]=rt;
        }
        a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
        a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
        pushup(x);pushup(rt);
    }
    void splay(int rt){
        int top=0;
        stack[++top]=rt;
        for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
        while(top)pushdown(stack[top--]);
        while(!isroot(rt)){
            int x=a[rt].f,y=a[x].f;
            if(!isroot(x)){
                if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
                else turn(x);
            }
            turn(rt);
        }
    }
    void access(int x){
        for(int i=0;x;i=x,x=a[x].f){
            splay(x);
            if(a[x].son[1])ST::update(id[a[a[x].son[1]].s],id[a[a[x].son[1]].s]+size[a[a[x].son[1]].s]-1,1,1);
            if(i)ST::update(id[a[i].s],id[a[i].s]+size[a[i].s]-1,-1,1);
            a[x].son[1]=i;
            if(i)a[i].f=x;
        }
    }
}
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void add(int x,int y){
    a[c].to=y;a[c].next=head[x];head[x]=c++;
    a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs1(int rt){
    son[rt]=0;size[rt]=1;
    for(int i=head[rt];i;i=a[i].next){
        int will=a[i].to;
        if(!deep[will]){
            deep[will]=deep[rt]+1;
            fa[will]=rt;
            dfs1(will);
            size[rt]+=size[will];
            if(size[son[rt]]<size[will])son[rt]=will;
        }
    }
}
void dfs2(int rt,int f){
    id[rt]=d++;top[rt]=f;
    if(son[rt])dfs2(son[rt],f);
    for(int i=head[rt];i;i=a[i].next){
        int will=a[i].to;
        if(will!=fa[rt]&&will!=son[rt])
        dfs2(will,will);
    }
}
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    return x;
}
void work(){
    int f,x,y;
    while(m--){
        f=read();x=read();
        if(f==1)LCT::access(x);
        if(f==2){
            y=read();
            int fa=LCA(x,y);
            printf("%d\n",(ST::query(id[x],id[x],1)+ST::query(id[y],id[y],1)-ST::query(id[fa],id[fa],1)*2+1));
        }
        if(f==3)printf("%d\n",ST::query(id[x],id[x]+size[x]-1,1));
    }
}
void init(){
    int x,y;
    n=read();m=read();
    for(int i=1;i<n;i++){
        x=read();y=read();
        add(x,y);
    }
    deep[1]=1;
    dfs1(1);
    dfs2(1,1);
    ST::buildtree(1,n,1);
    for(int i=1;i<=n;i++){
        ST::update(id[i],id[i],deep[i],1);
        LCT::a[i].s=i;
        LCT::a[i].f=fa[i];
    }
}
int main(){
    init();
    work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值