1. 程式人生 > >JSOI2008 星球大戰 洛谷P1197

JSOI2008 星球大戰 洛谷P1197

Description

很久以前,在一個遙遠的星系,一個黑暗的帝國靠著它的超級武器統治著整個星系。


某一天,憑著一個偶然的機遇,一支反抗軍摧毀了帝國的超級武器,並攻下了星系中幾乎所有的星球。這些星球通過特殊的以太隧道互相直接或間接地連線。


但好景不長,很快帝國又重新造出了他的超級武器。憑藉這超級武器的力量,帝國開始有計劃地摧毀反抗軍佔領的星球。由於星球的不斷被摧毀,兩個星球之間的通訊通道也開始不可靠起來。


現在,反抗軍首領交給你一個任務:給出原來兩個星球之間的以太隧道連通情況以及帝國打擊的星球順序,以儘量快的速度求出每一次打擊之後反抗軍佔據的星球的連通塊的個數。(如果兩個星球可以通過現存的以太通道直接或間接地連通,則這兩個星球在同一個連通塊中)。


Input

輸入檔案第一行包含兩個整數,N和M,分別表示星球的數目和以太隧道的數目。星球用0~N−1的整數編號。接下來的M行,每行包括兩個整數X,Y,表示星球x和星球y之間有“以太”隧道,可以直接通訊。接下來的一行為一個整數k,表示將遭受攻擊的星球的數目。接下來的k行,每行有一個整數,按照順序列出了帝國軍的攻擊目標。這k個數互不相同,且都在0到n−1的範圍內。


Output

第一行是開始時星球的連通塊個數。接下來的k行,每行一個整數,表示經過該次打擊後現存星球的連通塊個數。


Hint

1<=N<=2*M,1<=M<=200000,0<=X<Y。


Solution

因為正著不好想就倒著想,連通塊個數初始就是n-k,然後當他們不是一個父親且沒broke就dounion,然後後面詢問的時候再建立。最後記錄答案,cnt不用初始化,因為要在broke的基礎上建立。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 400005
using namespace std;
struct Edge{
    int u;
    int v;
    int time;
    int next;
}edge[maxn];
int first[maxn],last[maxn],FA[maxn],ans[maxn],orderr[maxn];
int node,n,m,x,y,k,cnt;
bool bro[maxn];
void addedge(int u,int v){
    edge[++node]=(Edge){u,v,0,0};
    if(first[u]==0)first[u]=node;
    else edge[last[u]].next=node;
    last[u]=node;
}
void init(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    for(int i=1;i<=n;i++){
        FA[i]=i;
    }
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d",&orderr[i]);
        bro[orderr[i]]=true;
    }
    cnt=n-k;
}
int dofind(int x){
    if(FA[x]==x)return FA[x];
    else return FA[x]=dofind(FA[x]);
}
int dounion(int x,int y){
    int dx=dofind(x),dy=dofind(y);
    if(dx!=dy){
        FA[dx]=FA[dy];
    }
}
bool dofinD(int x,int y){
    return dofind(x)==dofind(y);
}
void workk(){
    for(int i=1;i<=2*m;i++){
        int a=edge[i].u,b=edge[i].v;
        if(!bro[a]&&!bro[b]&&!dofinD(a,b)){
            cnt--;
            dounion(a,b);
        }
    }
    ans[k+1]=cnt;
    for(int i=k;i>=1;i--){
        cnt++;
        bro[orderr[i]]=false;
        for(int p=first[orderr[i]];p;p=edge[p].next){
            int v=edge[p].v;
            if(!bro[v]&&!dofinD(orderr[i],v)){
                cnt--;
                dounion(orderr[i],v);
            }
        }
        ans[i]=cnt;
    }
}
int main(){
    init();
    workk();
    for(int i=1;i<=k+1;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}