洛谷 P3348 [ZJOI2016]大森林 lct

题目描述

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

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

输入输出格式

输入格式:
第一行包含 2 个正整数n,m,共有n棵树和m个操作。
接下来 m 行,每行包含若干非负整数表示一个操作,操作格式为:
0 l r表示将第l棵树到第r棵树的生长节点下面长出一个子节点,子节点的标号为上一个0号操作叶子标号加1(例如,第一个 0 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<=105,M<=2105

输出格式:
输出包括若干行,按顺序对于每个小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

分析:
我们先排掉不合法的操作,操作0的编号是指上一次操作0编号+1,而不是当前子树编号加1。所以我们可以记录每个编号的区间,然后区间求交。
排掉所有不合法操作后,我们发现一个优美的性质,因为只能加点,又没有修改边权,所以对于一棵树,所有操作完成后再询问和中间询问其实是一样的。我们把操作离线,把 1 1 n的树依次搞出来,再完成当前树的询问。
因为是区间换根以及区间加点,对于区间 [l,r] [ l , r ] ,可以在 l l 处加入,再到r+1处删掉就可以了。
我们考虑把所有1操作的点 x x 都建一个虚点x(包括一开始的 1 1 节点),然后把他们连成一条链,这条链上的权值为0。我们把每个加点操作都挂在最近的1操作的虚点上面。我们可以这么看,这个点实际上加在树上的位置,就是这个点沿着虚链向上走到的节点,显然一开始走到的都是 1 1 节点,说明这个点的父亲是1。如果我们进行了换根操作,假设换了的根为 i i ′ ,那么把 i i ′ 向他前一个1操作的点的虚点的边断掉,连接 i i i就可以了。
具体来说这么做:
1. 1. 把操作离线,修改操作变为一个加入一个一个删除,查询不变。
2. 2. 每个一操作对应的 x x 建一个虚点x,并向前一个操作的点连一条权值为 0 0 的边,1的虚点向 1 1 连一条权值为0的边。
3. 3. 对与0操作的加入,直接向他最近的1操作的虚点连权值为1的边,删除操作相反。
4. 4. 对于1操作的加入,断开前一个1操作的虚点的边,连一条这个点的虚点到实点的边,权值为0。
5. 5. 查询直接树上查询两个实点的距离。可以发现,这棵树就是我们建出来的的树。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

const int maxn=3e5+7;
const int maxq=4e5+7;

using namespace std;

struct rec{
    int op,pos,num,u,v,ans;
}q[maxq];


struct tree{
    int fa,l,r,data,sum;
    bool rev;
}t[maxn];

int L[maxn],R[maxn];
int n,x,op,l,r,m,num1,cnt,num2;

bool cmp1(rec x,rec y)
{
    if (x.pos==y.pos) return x.op<y.op;
    return x.pos<y.pos;
}

bool cmp2(rec x,rec y)
{
    return x.num<y.num;
}

void updata(int x)
{
    int l=t[x].l,r=t[x].r;
    t[x].sum=t[l].sum+t[r].sum+t[x].data;      
}

bool isroot(int x)
{
    return (x!=t[t[x].fa].l) && (x!=t[t[x].fa].r);
}

void remove(int x)
{
    if (!isroot(x)) remove(t[x].fa);
    if (t[x].rev)
    {
        t[x].rev^=1;
        swap(t[x].l,t[x].r);
        if (t[x].l) t[t[x].l].rev^=1;
        if (t[x].r) t[t[x].r].rev^=1;
    }
}

void rttr(int x)
{
    int y=t[x].l;
    t[x].l=t[y].r;
    if (t[y].r) t[t[y].r].fa=x;
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].r=x;
    updata(x); updata(y);
}

void rttl(int x)
{
    int y=t[x].r;
    t[x].r=t[y].l;
    if (t[y].l) t[t[y].l].fa=x;
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].l=x;
    updata(x); updata(y);
}

void splay(int x)
{
    remove(x);
    while (!isroot(x))
    {
        int p=t[x].fa,g=t[p].fa;
        if (isroot(p))
        {
            if (x==t[p].l) rttr(p);
                      else rttl(p);
        }
        else
        {
            if (x==t[p].l)
            {
                if (p==t[g].l) rttr(p),rttr(g);
                          else rttr(p),rttl(g);
            }
            else
            {
                if (p==t[g].l) rttl(p),rttr(g);
                          else rttl(p),rttl(g);
            }
        }
    }
}

void access(int x)
{
    int y=0;
    while (x)
    {
        splay(x);
        t[x].r=y;
        updata(x);
        y=x;x=t[x].fa;
    }
}

void makeroot(int x)
{
    access(x);
    splay(x);
    t[x].rev^=1;
}

void link(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    t[x].fa=y;
}

void cut(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    t[x].fa=0; t[y].l=0;
    updata(y);
}

int main()
{
    scanf("%d%d",&n,&m);
    L[1]=1,R[1]=n;
    num1=1; 
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if (op==0)
        {
            scanf("%d%d",&l,&r);
            num1++;
            L[num1]=l; R[num1]=r;
            q[++cnt]=(rec){1,l,i,num1,0,0};
            q[++cnt]=(rec){2,r+1,i,num1,0,0};
        }
        if (op==1)
        {
            scanf("%d%d%d",&l,&r,&x);
            l=max(l,L[x]);
            r=min(r,R[x]);
            if (l>r) continue;
            if (x>num1) continue;
            q[++cnt]=(rec){3,l,i,x,0,0};
            q[++cnt]=(rec){4,r+1,i,x,0,0};
        }
        if (op==2)
        {
            scanf("%d%d%d",&x,&l,&r);
            q[++cnt]=(rec){5,x,i,l,r,0};
        }
    }
    num2=num1+1;    
    for (int i=1;i<=cnt;i++)
    {
        if (q[i].op==1) q[i].v=num2;
        if (q[i].op==2) q[i].v=num2;
        if (q[i].op==3) q[i].v=++num2;
        if (q[i].op==4) q[i].v=num2;
    }
    sort(q+1,q+cnt+1,cmp1); 
    link(1,num1+1);
    for (int i=num1+2;i<=num2;i++) link(i-1,i); 
    for(int i=1;i<=num1;i++) t[num2+i].data=1;  
    for (int i=1;i<=cnt;i++)
    {
        if (q[i].op==1)
        {
            link(q[i].u,q[i].u+num2);
            link(q[i].u+num2,q[i].v);
        }
        if (q[i].op==2)
        {
            cut(q[i].u,q[i].u+num2);
            cut(q[i].u+num2,q[i].v);
        }
        if (q[i].op==3)
        {
            cut(q[i].v-1,q[i].v);
            link(q[i].u,q[i].v);
        }
        if (q[i].op==4)
        {
            cut(q[i].u,q[i].v);
            link(q[i].v-1,q[i].v);
        }
        if (q[i].op==5)
        {
            makeroot(q[i].u);
            access(q[i].v);
            splay(q[i].v);
            q[i].ans=t[q[i].v].sum;
        }
    }
    sort(q+1,q+cnt+1,cmp2);
    for (int i=1;i<=cnt;i++)
    {
        if (q[i].op==5) printf("%d\n",q[i].ans);
    } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值