luogu1197 星球大战

题目描述

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。

但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。


不知为毛,题目描述出不全,贴网址,还是去luogu看吧

对于省选这种level的题目,只有在神奇的铭爷的讲解后才能做出来……,但是写代码的时候注意力有点散,发生了一些乱七八糟的错误,debug的时候各种晕。

看题

对于这个题面已经无语,真的,好长。大体意思就是给你一张图,每次给出一个要去除的点,求出去除后途中联通块的数量。

首先,对于200,000的查询,在线做法估计够呛,每次处理要做到log级别。。。联通块,做不到好像,至少要遍历一遍吧

所以,这里采取离线做法,先处理出最后的情况,然后倒着向图里加入点及其连着的边(要check一下另一个点是否在),若能够连接两个联通块,那么cnt–;最后倒序输出答案即可。对于联通块,可以用并查集(只能加,不能删的特性决定了必须倒着处理)维护。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=500000;
int n,m,q,f[maxn],cnt[maxn],ltk,ff=0,ans[maxn];
bool in[maxn];//点是否在图中
vector<int> list[maxn];//存边
struct ff{
    int sta,end;
}e[maxn];
int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}
int main()
{   
    memset(in,true,sizeof in);
    for(int i=1;i<=maxn-1000;i++) f[i]=i;
    int x,y;    
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        e[i].sta=x,e[i].end=y;
        list[x].push_back(i),list[y].push_back(i);
    }

    scanf("%d",&q);
    for(int i=1;i<=q;i++)   scanf("%d",&cnt[i]),in[cnt[i]]=false;

    ltk=n;  //一条边也没有
    for(int i=1;i<=m;i++)
    {   
        if(in[e[i].sta]&&in[e[i].end])
            {
                int f1=find(e[i].sta),f2=find(e[i].end);
                if(f1!=f2)
                ltk--,f[f1]=f2;
            }
    }
    ltk-=q;//去除已经不在的点
    ans[++ff]=ltk;

    for(int j=q;j>=1;j--)
    {
        in[cnt[j]]=true;ltk++;//加入一个点,在还没有加边的时候认为是一个独立的联通块
        for(int i=0;i<list[cnt[j]].size();i++)
        {
            int k=list[cnt[j]][i];
            if(in[e[k].sta]&&in[e[k].end])//点在不在图中
            {
                int f1=find(e[k].sta),f2=find(e[k].end);
                if(f1!=f2)
                ltk--,f[f1]=f2;
            }
        }
        ans[++ff]=ltk;
    }

    for(int i=ff;i>=1;i--)
        printf("%d\n",ans[i]);
    return 0;
}

倒序处理是一个很神奇的思想…

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值