首页 > 代码库 > luoguP1197 [JSOI2008]星球大战 x

luoguP1197 [JSOI2008]星球大战 x

P1197 [JSOI2008]星球大战

题目描述

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

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

输入输出格式

输入格式:

 

输入文件第一行包含两个整数,N (1 <= N <= 2M) 和M (1 <= M <= 200,000),分别表示星球的数目和以太隧道的数目。星球用0~N-1的整数编号。

接下来的M行,每行包括两个整数X, Y,其中(0<=X<>Y<N),表示星球X和星球Y之间有以太隧道。注意所有的以太隧道都是双向的。

接下来一行是一个整数K,表示帝国计划打击的星球个数。

接下来的K行每行一个整数X,满足0<=X<N,表示帝国计划打击的星球编号。帝国总是按输入的顺序依次摧毁星球的。

 

输出格式:

 

输出文件的第一行是开始时星球的连通块个数。

接下来的K行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

 

输入输出样例

输入样例#1:
8 130 11 66 55 00 61 22 33 44 57 17 27 63 6516357
输出样例#1:
111233

说明

[JSOI2008]

思路:

  联通块+并查集的逆用

  一开始假设所有的星球全部摧毁,然后进行离线处理(听着好高端的样子!)

坑点:

  一定要记得初始化head数组的值!!!因为会用到0号元素

上代码:

技术分享
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int M = 4e5 + 5;int heads[M];int n,m,k;int now=0,x,y;int dad[M],hit[M],answer[M];bool v[M];struct A{    int next,to;}h[M];void add(int pre,int to){    h[now].to=to;    h[now].next=heads[pre];    heads[pre]=now++;}int getdad(int Ms){    return dad[Ms] == Ms ? Ms : dad[Ms]=getdad(dad[Ms]);}void unions(int dads,int sons){    dad[getdad(dads)]=getdad(sons);}int main(){    scanf("%d%d",&n,&m);    for(int i=0;i<n;i++) dad[i]=i,heads[i]=-1;    for(int i=0;i<m;i++)    {        scanf("%d%d",&x,&y);        add(x,y),add(y,x);    }    scanf("%d",&k);    for(int i=0;i<k;i++)    {        scanf("%d",&hit[i]);        v[hit[i]]=true;        ///因为是逆序,表示当前星球已经被打击     }    for(int i=0;i<n;i++)    {        if(!v[i])        {            for(int j=heads[i];j!=-1;j=h[j].next)            {                int vc=h[j].to;                if(!v[vc])                    unions(i,vc);            }        }    }    for(int i=0;i<n;i++)        if(!v[i] && getdad(i) == i)            answer[k]++;///寻找最后一张图所剩余的连通块     for(int i=k-1;i>=0;i--)///因为存储的时候是正序存储,进行计算的时候应该逆序操作,所以颠倒顺序     {        int x=0;        ///x记录的是与当前被打击掉的星球相连的并且不被打击掉的请求         v[hit[i]]=false;///清除标记,因为是逆序.        for(int j=heads[hit[i]];j!=-1;j=h[j].next)        {            if(!v[h[j].to])            {                int a=getdad(hit[i]);                int b=getdad(h[j].to);                if(a!=b)                {                    unions(a,b);                    x++;                }            }        }        answer[i]=answer[i+1]-x+1;    }    for(int i=0;i<=k;i++)        printf("%d\n",answer[i]);    return 0;}
View Code

 

luoguP1197 [JSOI2008]星球大战 x