洛谷 p1197 星球大战

传送门:星球大战

致敬老鹰乐队,播放上一曲Hotel California,边听边看。

题意:反叛军想要摧毁帝国的星球。帝国的星球之间有着以太作为连接,以次帝国的星球直接或者间接相连着。反叛军每摧毁一个星球,其星球就不复存在,其和其他星球的以太隧道也将断开。给出一个k 是反叛军将要摧毁的星球数目,然后依次告诉你这k个即将被摧毁的星球的编号。问每次叛军摧毁一个星球后,有多少个连通块,如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中。

起初,我是想先把图建立好以后,每次摧毁一个星球,就将这个星球(点)在图中抹掉,相连的边也擦除,然后跑Tarjan求连通分量。emmm...应该可行。来吧,仔细想想,这么做不会TLE吗?绝对会呀,题目的标签是并查集,这个思路不行。那怎么办,看到别人说,并查集是用来建立边与边之间关系的。是一种和谐的结构,摧毁边,并查集是做不来的。那么为什么不反过来想,摧毁一条边看成建立一条边,我们从最后一次询问,即最后一个星球被毁灭开始,建立边,一直到第一个要被摧毁的星球,期间我们求的连通块的个数反着输出就是答案了。

/*
 * Do not go gentle into that good night
 *                                    ----Dylan Thomas
 * Author:  looooop
 * Created Time:  2018年12月11日 星期二 19时45分44秒
 * File Name: p1197.cpp
 */
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <time.h>

using namespace std;
#define lson 2*i
#define rson 2*i+1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
#define MAX 400005
struct Edge{
    int from;
    int to;
    int next;
};
int head[MAX];int fa[MAX];
int mark[MAX];int echo[MAX];
int attack[MAX];int ans[MAX];int tot;
Edge G[MAX];
int cnt;
int n,m;
int x,y;
int k;
void add(int u,int v){
    cnt++;
    G[cnt].from = u;
    G[cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
}
int Find(int x){
    if(x != fa[x])
        return fa[x] = Find(fa[x]);
    return x;
}
void init(){
    for(int i = 0; i <= n; i++)
        fa[i] = i;
    MEM(head,0);
    tot = n-k;        //逆着并查集时,所有k个星球都被摧毁后初始化帝国的连通块个数
}
int main(int argc,char *argv[]) {
    scanf("%d%d",&n,&m);
    //init();
    for(int i = 0; i <= n; i++)
        fa[i] = i;
    MEM(head,0);
    for(int i = 1; i <= m; i++){
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    scanf("%d",&k);
    tot = n-k;
    //init();
    for(int i = 1; i <= k; i++){
        int victim;
        scanf("%d",&victim);  //受害者,要被摧毁的星球
        mark[victim] = 1;    //mark标记,是否已被摧毁
        attack[i] = victim;    //victim,存在数组,方便后面逆着做
    }
    for(int i = 1; i <= m*2; i++){
            if(mark[G[i].from]==0 and mark[G[i].to]==0){
                if(Find(G[i].from) != Find(G[i].to))
                    tot--,fa[Find(G[i].from)] = fa[Find(G[i].to)];
            }
    }
    ans[k+1] = tot;
    for(int os = k; os >= 1; os--){
        int st = attack[os];
        mark[st] = 0;
        tot++;
        for(int i = head[st];i; i = G[i].next){
            int v = G[i].to;
            int Find_st = Find(st);
            int Find_v = Find(v);
            if(mark[G[i].to]==0 and fa[Find_st]!=fa[Find_v]){
                tot--;
                fa[Find_v] = fa[Find_st];
            }
        }
        ans[os] = tot;
    }
    for(int i = 1; i <= k+1; i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值