【hautoj 1285 】 军团再临 【 思维+并查集】

题目描述

燃烧军团大举入侵艾泽拉斯,现以在艾星建立了大量的军事要塞,这些要塞通过若干个道路直接或间接连接。如果两个城市是连通的,那么它们处于同一连通块之中。现在抗魔联军发明了一种威力巨大的法术,每发动一次将会直接摧毁一个军事要塞,同时也摧毁与他相连的道路。每进行一次打击,联军需要知道未被摧毁的要塞的连通块数。联军会给出最初要塞的连通情况以及打击顺序,请快速计算每次打击之后未被摧毁的要塞的连通块数量。
输入

输入第一行为两个整数n,m分别表示要塞个数和道路个数。1<=n<=100000,1<=m<=200000。
接下来m行每行两个整数x,y (1<=x,y<=n)表示要塞x和要塞y之间有一条道路连接。
接下来一行为一个整数k表示将会打击的要塞数量。1<=k<=n。
接下来k行每行一个整数v表示会打击的城市这k个数互不相同,1<=v<=n。
输出

第一行输出最初的连通块个数,后k行一次输出每次打击后的结果。
样例输入

8 13
1 2
2 7
7 6
6 1
1 7
2 3
3 4
4 5
5 6
8 2
8 3
8 7
4 7
5
2
7
4
6
8
样例输出

1
1
1
2
3
3

要能够想到 是倒着来求解。
代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int MAXN = 100000+10;
const int MAXM = 200000;
struct Edge {
    int from,to,nexts;
}edge[MAXM<<2];
int head[MAXN],top;
int pre[MAXN];
int n,m;
void init(){
    memset(head,-1,sizeof(head));
    top=0;
    for(int i=0;i<=n;i++)  pre[i]=i;
} 
void addedge(int a,int b){
    Edge e={a,b,head[a]};
    edge[top]=e;head[a]=top++;
}
int Find(int x){ return x==pre[x]?x:pre[x]=Find(pre[x]); }
void join(int a,int b){
    a=Find(a);b=Find(b);
    if(a!=b) pre[a]=b;
}

int hit[MAXN];
map<int,bool>vis;
map<int,int>M;
int ans[MAXN];
void solve(){
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b);
        addedge(b,a); 
    } 
    int k;cin>>k;
    for(int i=1;i<=k;i++) {
        scanf("%d",&hit[i]);
        vis[hit[i]] =1;
    }

    for(int now=1;now<=n;now++){
        if(vis[now]) continue;
        for(int i=head[now];i!=-1;i=edge[i].nexts){
            Edge e=edge[i];
            if(vis[e.to]) continue;
            join(e.from,e.to);
        }
    }
    //puts("==========");
    for(int i=1;i<=n;i++) {
        if(vis[i]) continue;
        M[Find(i)]++;
    }
    ans[k+1]=M.size();
//  printf(" nas[k+1] %d\n",ans[k+1]);
    for(int now=k;now>=1;now--){
        vis[hit[now]]=0;  ans[now]=ans[now+1]+1;// 因为初始多一个点,肯定多一个联通快
        for(int i=head[hit[now]];i!=-1;i=edge[i].nexts){
            Edge e=edge[i];
            if(vis[e.to]) continue;
            int t1=Find(e.from);int t2=Find(e.to);
            if(t1!=t2){ //合并一次,联通快肯定少一个
                 ans[now]--;  pre[t1]=t2;  
            }
        } 
    }
    for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]) ;
}
int main(){
    scanf("%d%d",&n,&m);
    init();
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值