bzoj 4573大森林

题目大意

初始给出n棵节点数为1且生长节点标号为1的树,有三个操作:
0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,标号为当前0的操作数+1;
1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l≤i≤r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响;
2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离。
N<=10^5,M<=2*10^5

离线,lct

先定义实点值为1,虚点值为0
对于每一个生长节点,我们新建一个虚点;一个0操作,在对应的虚点下面建一个实点。
1操作可能存在不合法的部分,但由于0操作是对于一个区间修改的,那么1操作的修改区间显然也是连续的,很容易得到实际要修改的区间,不需要考虑不合法情况。
1操作在l位置挂一个插入,在r+1位置挂一个删除。
对于插入操作,我们直接把新建节点向x连条边;
对于删除操作,我们把新建节点和x的边删掉,再向上一个生长节点的虚点连条边。
可以机智的发现,0操作其实可以一开始就全处理掉,并不会影响答案。
以上用lct就可以解决。

代码

#include<cstring>
#include<algorithm>
#include<cstdio>
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b) 
using namespace std;
char c;int w;
inline int read(){
    w=0;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar()) w=w*10+c-48;
    return w;
}
const int maxn=4e5+5;
struct ar{
    int pos,op,x,y;
}d[maxn];
bool cmp(ar x,ar y){ 
    return x.pos<y.pos||(x.pos==y.pos&&x.op<y.op); 
}  
int i,j,n,m,q[maxn],cn,now,last,b[maxn],tree[maxn][2],sum[maxn],num[maxn],fa[maxn],tfa[maxn];
int ans[maxn],l[maxn],cnt=1,r[maxn],tot;
bool bz[maxn];
bool pd(int x) {return (tree[fa[x]][0]==x)?0:1;}
void re(int x){
    if (!x)return;
    swap(tree[x][0],tree[x][1]),bz[x]^=1;
}
void ins(int x){num[++cn]=sum[cn]=x;}
void update(int x){
    int l=tree[x][0],r=tree[x][1];
    sum[x]=sum[l]+sum[r]+num[x];
}
void rotate(int x){
    int y=fa[x],z=pd(x),z1=pd(y);
    if (tree[y][z]=tree[x][z^1]) fa[tree[y][z]]=y;
    if (fa[x]=fa[y]) tree[fa[x]][z1]=x;
    if (tfa[y]) tfa[x]=tfa[y],tfa[y]=0;
    tree[x][z^1]=y,fa[y]=x,update(y);
}
void clear(int x){
    int l=tree[x][0],r=tree[x][1];
    if (bz[x]) re(l),re(r);
    bz[x]=0;
}
void chu(int x,int y){
    q[0]=0;
    for(;x!=y;x=fa[x]) q[++q[0]]=x;
    for(;q[0];q[0]--) clear(q[q[0]]);
}
void splay(int x,int y){
    chu(x,y);
    while (fa[x]!=y){
        int f=fa[x];
        if (fa[f]!=y){
            if (pd(x)==pd(f)) rotate(f);else rotate(x);
        }rotate(x);
    }update(x);
}
void access(int x){
    last=0;int y;
    for(;x;last=x,x=tfa[x]){
        splay(x,0);
        y=tree[x][1];
        if (y) tree[x][1]=0,fa[y]=0,tfa[y]=x;
        if (last) tree[x][1]=last,fa[last]=x,tfa[last]=0;
        update(x);
    }
}
void makeroot(int x){
    access(x);
    splay(x,0);
    re(x);
}
void link(int x,int y){
    makeroot(x);
    splay(x,0);
    tfa[x]=y;
}
void cut(int x){  
    access(x); 
    splay(x,0); 
    tree[x][0]=fa[tree[x][0]]=0; 
    update(x);  
}  
int main(){
    n=read(),m=read();
    ins(1),b[1]=l[1]=1,r[1]=n;
    ins(0),now=2,link(2,1);
    fo(i,1,m){
        int o=read();
        if (!o){
            l[++cnt]=read(),r[cnt]=read();
            ins(1),b[cnt]=cn;
            d[++tot]=(ar){1,i-m,cn,now};
        }else
        if (o==1){
            int x=read(),y=read(),k=read();
            x=max(x,l[k]),y=min(y,r[k]);
            if (x<=y){
                ins(0);
                link(cn,now);
                d[++tot]=(ar){x,i-m,cn,b[k]};
                d[++tot]=(ar){y+1,i-m,cn,now};
                now=cn;
            }
        }else{
            int x=read(),u=read(),v=read();
            d[++tot]=(ar){x,i,b[u],b[v]};
        }
    }
    sort(d+1,d+1+tot,cmp);
    memset(ans,-1,sizeof(ans));
    j=1;
    fo(i,1,n){
        for(;j<=tot&&d[j].pos==i;j++)if (d[j].op>0){
            int x=d[j].x,y=d[j].y;
            access(x),splay(x,0),ans[d[j].op]=sum[x];
            access(y),splay(y,0),ans[d[j].op]+=sum[y];
            access(last),splay(last,0),ans[d[j].op]-=sum[last]<<1;
        }else cut(d[j].x),link(d[j].x,d[j].y);
    }
    fo(i,1,m) if (ans[i]!=-1) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值