【GDOI2018模拟9.23】动态图

本文介绍了一种使用动态树(LCT)解决特定生成树问题的方法,通过维护最大生成树来更新答案,并讨论了离线和在线两种情况下算法的设计与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

5 3 0
1 4 5
1 2 4
3 1 2

Sample Output

3

Solution

动态图?用动态树来做
先只考虑离线
所有询问按照右端点排序
按照边从前往后加入
相当于维护一个生成树
如果一条边加入会形成环,那么就替换掉这个环上最早出现的边
为什么是正确的呢?
询问的右端点固定,左端点不同,我的边尽量靠近右端点,就可以保证答案最优
答案就是n-这些边加入了LCT的数量
那么维护的就是最大生成树
每次加边替换掉某条边的时候记录下来
再删边的时候就可以还原回去
用一个树状数组或者线段树记录每条边是否出现就可以维护答案了
如果你理解了的话,就会发现在线也是一样的
把维护答案的数据结构改成可以可持久化的(可持久化线段树)就行了
这题想到不难,难的是你需要种两棵树
另附出数据程序,在标程的下面

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1010000
#define M 2010000
using namespace std;
int n,m,to,cty,a[N],t1=0,t2=0,b[M],c[M],rev[M],p[M],fa[M],t[M][2],s[M],size[M],d[M],ans,bz[N],r[N];
struct node{
    int x,y,z;
}bi[N];
struct note{
    int l,r,d;
}ta[M*5];
void ins(int v,int i,int j,int x,int y)
{
    ta[v].d+=y;
    if(i==j) return;
    int mid=(i+j)/2;
    if(x<=mid) ta[++to]=ta[ta[v].l],ta[v].l=to,ins(ta[v].l,i,mid,x,y);
    else ta[++to]=ta[ta[v].r],ta[v].r=to,ins(ta[v].r,mid+1,j,x,y);
}
int gans(int v,int i,int j,int x,int y)
{
    if(v==0) return 0;
    if(i==x&&j==y) return ta[v].d;
    int mid=(i+j)/2;
    if(y<=mid) return gans(ta[v].l,i,mid,x,y);
    else if(x>mid) return gans(ta[v].r,mid+1,j,x,y);
         else return gans(ta[v].l,i,mid,x,mid)+gans(ta[v].r,mid+1,j,mid+1,y);
}
void update(int x)
{
    size[x]=size[t[x][0]]+size[t[x][1]]+b[x];
    if(c[x]) d[x]=c[x];else d[x]=2147483647;
    if(t[x][0]) d[x]=min(d[x],d[t[x][0]]);
    if(t[x][1]) d[x]=min(d[x],d[t[x][1]]);
}
void down(int x)
{
    if(!rev[x]) return;
    swap(t[x][0],t[x][1]);
    rev[t[x][0]]^=1;rev[t[x][1]]^=1;
    rev[x]=0;
}
void xc(int x)
{
    for(;x;x=fa[x]) s[++s[0]]=x;
    for(;s[0];s[0]--) down(s[s[0]]);
}
int lr(int x){return x==t[fa[x]][1];}
void rotate(int x)
{
    int y=fa[x],k=lr(x);
    t[y][k]=t[x][1-k];if(t[x][1-k]) fa[t[x][1-k]]=y;
    fa[x]=fa[y];
    if(fa[y]) t[fa[y]][lr(y)]=x;
    else p[x]=p[y],p[y]=0;
    t[x][1-k]=y;fa[y]=x;
    update(y);update(x);
}
void splay(int x,int y)
{
    xc(x);
    while(fa[x]!=y)
    {
        if(fa[fa[x]]!=y)
            if(lr(x)==lr(fa[x])) rotate(fa[x]);
            else rotate(x);
        rotate(x);
    }
}
void access(int x)
{
    int y=0;
    while(x)
    {
        splay(x,0);
        fa[t[x][1]]=0;p[t[x][1]]=x;
        t[x][1]=y;fa[y]=x;p[y]=0;
        update(x);
        y=x;x=p[x];
    }
}
void makeroot(int x)
{
    access(x);splay(x,0);rev[x]^=1;
}
void link(int x,int y)
{
    makeroot(x);p[x]=y;
}
void cut(int x,int y)
{
    makeroot(x);access(y);splay(y,0);
    t[y][0]=fa[x]=p[x]=0;update(y);
}
int main() 
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    scanf("%d%d%d",&n,&m,&cty);to=m;
    int tot=0,bkill=0;
    memset(a,0,sizeof(a));
    int j=0,hzj=0;
    fo(i,1,m)
    {
        int ac;scanf("%d",&ac);
        if(ac==2)
        {
            hzj++;ta[hzj]=ta[hzj-1];
            int eb=tot;while(bz[eb]) eb--;bz[eb]=1;
            int z=eb;bkill++;bi[j].z=bkill;j--;
            if(bi[z].x==bi[z].y) continue;
            cut(bi[z].x,z+n);cut(z+n,bi[z].y);ins(hzj,1,m,z,-1);
            z=a[z];
            if(z==0) continue;
            if(bi[z].x==bi[z].y) continue;
            c[z+n]=z;link(bi[z].x,z+n);link(z+n,bi[z].y);ins(hzj,1,m,z,1);
        }
        if(ac==1)
        {
            hzj++;ta[hzj]=ta[hzj-1];
            int x,y,z=++tot;r[z]=hzj;
            scanf("%d%d",&x,&y);
            if(cty) x=(x^ans)%n+1,y=(y^ans)%n+1;
            bi[z].x=x;bi[z].y=y;bi[++j].z=bkill;
            if(x==y) continue;
            b[y]=1;makeroot(x);access(y);
            splay(x,0);
            if(size[x]==0)
            {
                c[z+n]=z;link(x,z+n);link(z+n,y);ins(hzj,1,m,z,1);
            }
            else
            {
                splay(x,0);int k=d[x];
                cut(bi[k].x,k+n);cut(k+n,bi[k].y);ins(hzj,1,m,k,-1);
                a[z]=k;c[z+n]=z;ins(hzj,1,m,z,1);
                link(x,z+n);link(z+n,y);
            }
            b[y]=0;
        }
        if(ac==3)
        {
            int x,y;scanf("%d%d",&x,&y);
            if(cty) x=(x^ans)%j+1,y=(y^ans)%j+1;
            if(x>y) swap(x,y);
            x+=bi[x].z;y+=bi[y].z;
            ans=gans(r[y],1,m,x,y);
            ans=n-ans;
            printf("%d\n",ans);
        }
    }
}

出数据

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1010000
using namespace std;
int tot;
int main()
{
    freopen("graph.in","w",stdout);
    int n=10,m=20;//修改这里获得不同的数据规模
    printf("%d %d 0\n",n,m);
    srand((unsigned)time(0));
    fo(i,1,m)
    {
        int z=rand()%3+1;
        while(tot<=2&&(z==2||z==3)) z=rand()%3+1;
        if(z==1)
        {
            int x=rand()%n+1,y=rand()%n+1;
            printf("%d %d %d\n",z,x,y);
            tot++;
        }
        if(z==2) printf("%d\n",z),tot--;
        if(z==3)
        {
            int x=rand()%(tot-1)+1,y=rand()%(tot-x)+1+x;
            printf("%d %d %d\n",z,x,y);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值