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

洛谷P1197 [JSOI2008]星球大戰

題目描述

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

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

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

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

輸入輸出格式

輸入格式:

輸入檔案第一行包含兩個整數,NNN (1<=N<=2M1 < = N < = 2M1<=N<=2M) 和 MMM (1<=M<=200,0001 < = M < = 200,0001<=M<=200,000),分別表示星球的數目和以太隧道的數目。星球用 000 ~ N−1N-1N1 的整數編號。

接下來的 MMM 行,每行包括兩個整數 XXX, YYY,其中( 0<=X<>Y0 < = X <> Y0<=X<>Y 表示星球 xxx 和星球 yyy 之間有 “以太” 隧道,可以直接通訊。

接下來的一行為一個整數 kkk ,表示將遭受攻擊的星球的數目。

接下來的 kkk 行,每行有一個整數,按照順序列出了帝國軍的攻擊目標。這 kkk 個數互不相同,且都在 000 到 n−1n-1n1 的範圍內。

輸出格式:

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

 

說明

 

[JSOI2008]

分割線

看到這道題,我第一時間想到了並查集,但是我發現了一個問題,就是並查集並不支援刪邊!

所以我推了一遍樣例,突然想到,並查集雖然不支援刪邊,但是支援加邊啊,所以這個問題就變成了從後向前依次加邊並記下來,然後倒著輸出就可以了。

一開始先把所有的邊存上(用鏈式前向星),然後把要加的邊打個標記,並算一下還剩多少個點沒被刪,記一個tal為n-k,之後遍歷所有的邊,如果這條邊的兩個端點有一個是要被刪掉的,就continue,跳過這個迴圈,如果兩個端點都還在,就把他們合併到同一個並查集裡,然後tal-1,連通塊減1。

每次加一個點都用一個res陣列記下來每次連通塊的個數tal的值,最後輸出res陣列就好啦,qwq這道題好久之前就分析完了,但是一直沒寫,今天一發入魂,ac了這道題!

注意:!!!鏈式前向星無向圖存邊一定要開兩倍資料範圍!

程式碼如下(自認為寫的程式碼很優美):

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<iostream>
#include<algorithm>
#define M 420000
using namespace std;
int n,m,k;
int x[M],y[M];
int h[M];
int fa[M];
int to[M],head[M],nex[M],tot;
int vis[M];
int res[M];
void add(int x,int y)
{
    to[++tot]=y;
    nex[tot]=head[x];
    head[x]=tot;
}
int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        add(x[i],y[i]);
        add(y[i],x[i]);
    }
    for(int i=0;i<n;i++)
    {
        fa[i]=i;
    }
    scanf("%d",&k);
    for(int i=0;i<k;i++)
    {
        int x;
        scanf("%d",&h[i]);
        vis[h[i]]=1;
    }
    int tal=n-k;
    for(int i=0;i<m;i++)
    {
        if(vis[x[i]]||vis[y[i]])
        {
            continue;
        }
        else
        {
            int dx=find(x[i]);
            int dy=find(y[i]);
            if(dx!=dy)
            {
                fa[dx]=dy;
                tal--;
            }
        }
    }
    res[k]=tal;
    for(int i=k-1;i>=0;i--)
    {
        if(vis[h[i]])
        {
            vis[h[i]]=0;
            tal++;
        }
        for(int j=head[h[i]];j!=-1;j=nex[j])
        {
            int g=to[j];
            if(vis[g])continue;
            int dx=find(h[i]);
            int dy=find(g);
            if(dx!=dy)
            {
                fa[dx]=dy;
                tal--;
            }
        }
        res[i]=tal;
    }
    for(int i=0;i<=k;i++)
    {
        printf("%d\n",res[i]);
    }
    return 0;
}

end.謝謝閱讀