【JSOI 2008】星球大战starwar 离线+并查集

Description
  很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的
机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直
接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划
地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首
领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每
一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则
这两个星球在同一个连通块中)。
Input
  输入文件第一行包含两个整数,N (1 < = N < = 2M) 和M (1 < = M < = 200,000),分别表示星球的
数目和以太隧道的数目。星球用 0 ~ N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0 < = X <>
Y 表示星球x和星球y之间有“以太”隧道,可以直接通讯。接下来的一行为一个整数k,表示将遭受攻击的星球的
数目。接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这k个数互不相同,且都在0到n-1的范
围内。
Output
  输出文件的第一行是开始时星球的连通块个数。接下来的N行,每行一个整数,表示经过该次打击后现存星球
的连通块个数。

Sample Input

8 13

0 1

1 6

6 5

5 0

0 6

1 2

2 3

3 4

4 5

7 1

7 2

7 6

3 6

5

1

6

3

5

7

Sample Output

1

1

1

2

3

3

题解:
这个题目嗯,看到联通块,嗯,用并查集,可是有删点,还要维护联通性,艹,额,我最开始想到的是删点并查集,后来发现额,这样没办法维护联通性啊。怎么办,嗯,想到原来的一句名言:没思路的时候不如倒过来看看?然后发现,好,我把操作反过来,把删点改成加边,我只用把最后图里面还存在的点维护联通性,然后加个点,就相应的加一些边进去。没了,╮(╯^╰)╭。这么简单,我居然写了一个多小时,我真的是越来越弱。
代码:

#include <iostream>
#include <cstdio>
#include <stack>

const int MAXN = 500000;

using namespace std;

int N,M,K,e = 1,scc;
int sum[MAXN],Fa[MAXN],head[MAXN],d[MAXN],ans[MAXN];
bool U[MAXN];

struct node{
    int v,next;
}edge[MAXN];

inline void addedge(int u,int v){
    edge[e] = (node){v,head[u]};head[u] = e++;
}

int Find(int x){
    return Fa[x] == x ? x : Fa[x] = Find(Fa[x]);
}

inline void Union(int x,int y){
    int fx = Find(x);
    int fy = Find(y);

    if(sum[fx] > sum[fy]){
        Fa[fy] = fx;
        sum[fx] += sum[fy];
    }
    else {
        Fa[fx] = fy;
        sum[fy] += sum[fx];
    }
}

inline int read(){
    int x = 0;char ch = getchar();
    while(ch < '0' || '9' < ch){ch = getchar();}
    while('0' <= ch&&ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}   

inline void init(){
    N = read();M = read();

    int u,v;

    scc = N;

    for(int i = 1;i <= M;i++){
        u = read();v = read();
        addedge(u,v);addedge(v,u);
    }

    K = read();

    for(int i = 1;i <= K;i++){
        d[i] = read();
        U[d[i]] = true;
    }
}

int main()
{
    init();

    for(int i = 0;i < N;i++) Fa[i] = i,sum[i] = 1;

    for(int i = 0;i < N;i++)
    {

        for(int j = head[i];j;j = edge[j].next){
            int v = edge[j].v;
            if(!U[i] && !U[v]){
                if(Find(i) != Find(v)){
                Union(i,v);
                scc--;
                }
            }
        }
    }

    ans[K] = scc - K;

    for(int i = K;i >= 1;i--)
    {
        U[d[i]] = false;
        for(int j = head[d[i]];j;j = edge[j].next){
            int v = edge[j].v;
            if(!U[v]){
            if(Find(d[i]) != Find(v)){
                Union(d[i],v);
                scc--;
            }
            }
        }
        ans[i - 1] = scc - (i-1);
    }

    for(int i = 0;i <= K;i++)
    printf("%d\n",ans[i]);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值