JZOJ3663. 【SHTSC2014】神奇化合物

题目描述

这里写图片描述
n≤5000,q≤10000

题目大意

就是有n个点,
有加边,删边操作,
询问当前联通块格式。

题解

边虽然很多,但是实际上有用的不多。
假设有两个联通块,之间存在着两条边。
暂时不考虑联通块里面的边的变化,
那么这两条边中较早删去的那一条边就没有意义了。

基于这个思想,
给每一条边有个边权,
即q-它被删去的时间,如果某一条边不被删去,那么它的边权就是0。
有了边权就可以做最小生成树。

每次只保留最多n-1条边,
删除就判断这条边是否最小生成树上面,是就直接去掉,否则就不用管。
加入一条边就O(n)的复杂度重构一棵最小生成树,
询问就可以直接输出了。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 5003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    int x,y,z;
};

bool operator ==(node a,node b)
{
    return a.x==b.x && a.y==b.y && a.z==b.z;
}

int n,m,tot,f[N][N],x,y,cz[N*2],tr,opt,q;
int fa[N],ans;
node use[N],t[N],a[N*40+10000];

bool cmp(node a,node b)
{
    return a.z<b.z;
}

int get(int x)
{
    return fa[x]=(fa[x]==x?x:get(fa[x]));
}

void mst(node p)
{
    int m=0;
    for(int i=1;i<=tr;i++)
    {
        if(p.z<use[i].z)t[++m]=p,p.z=2147483647;
        t[++m]=use[i];
    }

    if(p.z!=2147483647)t[++m]=p;

    for(int i=1;i<=n;i++)
        fa[i]=i;

    tr=0;
    for(int i=1;i<=m;i++)
    {
        x=get(t[i].x);
        y=get(t[i].y);
        if(x!=y)
        {
            fa[x]=y;
            use[++tr]=t[i];
        }
    }
}

int main()
{
    freopen("compound.in","r",stdin);
    freopen("compound.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<=m;i++)
        read(a[i].x),read(a[i].y),f[a[i].x][a[i].y]=f[a[i].y][a[i].x]=i;

    read(q);tot=m;
    for(int i=1;i<=q;i++)
    {
        for(ch=G();ch!='Q' && ch!='D' && ch!='A';ch=G());
        if(ch=='A')
        {
            cz[i]=1;tot++;
            read(a[tot].x);
            read(a[tot].y);
            f[a[tot].x][a[tot].y]=f[a[tot].y][a[tot].x]=tot;
        }else
        if(ch=='D')
        {
            cz[i]=2;tot++;
            read(x);
            read(y);
            a[f[x][y]].z=q+m-i;
            a[tot]=a[f[x][y]];
        }else
        if(ch=='Q')cz[i]=3;
    }

    for(int i=1;i<=n;i++)
        fa[i]=i;

    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=n;i++)
        fa[i]=i;

    tr=0;
    for(int i=1;i<=m;i++)
    {
        x=get(a[i].x);
        y=get(a[i].y);
        if(x!=y)
        {
            fa[x]=y;
            use[++tr]=a[i];
        }
    }

    opt=m;
    for(int i=1;i<=q;i++)
    {
        if(cz[i]==1)mst(a[++opt]);else
        if(cz[i]==2)
        {
            opt++;
            for(int j=1;j<=tr;j++)
                if(a[opt]==use[j])
                {
                    for(int k=j+1;k<=tr;k++)
                        use[k-1]=use[k];
                    tr--;
                    break;  
                }
        }else
        if(cz[i]==3)write(n-tr),P('\n');
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值