洛谷P3348 [ZJOI2016]大森林

题目描述

小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。

小Y掌握了一种魔法,能让第l棵树到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?

输入输出格式

输入格式:

第一行包含 2 个正整数 n,m,共有 n 棵树和 m 个操作。接下来 m 行,每行包含若干非负整数表示一个操作,操作格式为:0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例如,第一个 0 号操作产生的子节点标号为 2), l 到 r 之间的树长出的节点标号都相同。保证 1<=l<=r<=n 。1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l<=i<=r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响。保证 1<=l<=r<=n , x 不超过当前所有树中节点最大的标号。2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离,也就是在第 x 棵树中从节点 u 和节点 v 的最短路上边的数量。保证1<=x<=n,这棵树中节点 u 和节点 v 存在。N<=10^5,M<=2*10^5

输出格式:

输出包括若干行,按顺序对于每个小Y的询问输出答案

输入输出样例

输入样例#1: 
5 5
0 1 5
1 2 4 2
0 1 4
2 1 1 3
2 2 1 3
输出样例#1: 
1
2








离线+LCT。。。

我们对于每一个1操作建一个虚点。 
为了区分真实存在的点与辅助用的虚点,我们引入点权,虚点为0实点为1。 
每一个0操作对应点都挂在其前面时间最晚的1操作对应虚点下。 
当一个1操作当前不生效时,其也挂在其前面时间最晚的1操作对应虚点下,但若其生效了,我们把它挂在其对应点下。 
这样做显然保证了正确性。 
那么随手打个LCT就能做了。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 1000010
using namespace std;
int n,m,c=1,now=0,top=0;
int s[MAXN],t[MAXN],w[MAXN],ans[MAXN];
bool used[MAXN];
struct Link_Cut_Tree{
    int size,stack[MAXN];
    struct node1{
        int son[2];
        int f,v,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 newnode(int x){
        int rt=++size;
        a[rt].s=a[rt].v=x;
    }
    inline void pushup(int rt){
        if(!rt)return;
        a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+a[rt].v;
    }
    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 rt){
        for(int i=0;rt;i=rt,rt=a[rt].f){
            splay(rt);
            a[rt].son[1]=i;
            pushup(rt);
        }
    }
    int access_lca(int rt){
        int i;
        for(i=0;rt;i=rt,rt=a[rt].f){
            splay(rt);
            a[rt].son[1]=i;
            pushup(rt);
        }
        return i;
    }
    inline void split(int x){access(x);splay(x);}
    inline void link(int x,int y){splay(x);a[x].f=y;}
    inline void cut(int x){split(x);a[a[x].son[0]].f=0;a[x].son[0]=0;pushup(x);}
}LCT;
struct node2{
    int id,f,x,y;
}a[MAXN];
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;
}
bool cmp(const node2 &x,const node2 &y){
    if(x.id==y.id)return x.f<y.f;
    return x.id<y.id;
}
inline void add(int id,int f,int x,int y){
    top++;
    a[top].id=id;a[top].f=f;a[top].x=x;a[top].y=y;
}
void work(){
    int x,y,lca;
    for(int i=1,k=1;i<=n;i++)
    for(;a[k].id==i;k++){
        int t=a[k].f;
        if(t>0){
            x=a[k].x;y=a[k].y;
            LCT.split(x);ans[t]+=LCT.a[x].s;
            lca=LCT.access_lca(y);LCT.splay(y);ans[t]+=LCT.a[y].s;
            LCT.split(lca);ans[t]-=LCT.a[lca].s*2;
        }
        else{
            LCT.cut(a[k].x);LCT.link(a[k].x,a[k].y);
        }
    }
    for(int i=1;i<=m;i++)if(used[i])printf("%d\n",ans[i]);
}
void init(){
    int f,x,y,k;
    n=read();m=read();
    LCT.newnode(1);LCT.newnode(0);LCT.link(2,1);
    now=2;
    w[1]=s[1]=1;t[1]=n;
    for(int i=1;i<=m;i++){
        f=read();x=read();y=read();
        if(f==0){
            c++;
            LCT.newnode(1);
            w[c]=LCT.size;s[c]=x;t[c]=y;
            add(1,i-m,LCT.size,now);
        }
        if(f==1){
            k=read();x=max(x,s[k]);y=min(y,t[k]);
            if(x<=y){
                LCT.newnode(0);LCT.link(LCT.size,now);
                add(x,i-m,LCT.size,w[k]);
                add(y+1,i-m,LCT.size,now);
                now=LCT.size;
            }
        }
        if(f==2){
            k=read();
            used[i]=true;
            add(x,i,w[y],w[k]);
        }
    }
    sort(a+1,a+top+1,cmp);
}
int main(){
    init();
    work();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值