BZOJ - 1015【并查集】

1015: [JSOI2008]星球大战starwar

Time Limit: 3 Sec  Memory Limit: 162 MB
Submit: 5721  Solved: 2639
[Submit][Status][Discuss]

Description

  很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的

机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直

接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划

地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首

领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每

一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则

这两个星球在同一个连通块中)。

Input

  输入文件第一行包含两个整数,N (1  < =  N  < =  2M) 和M (1  < =  M  < =  200,000),分别表示星球的

数目和以太隧道的数目。星球用 0 ~ N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0 < = X <>

Y 表示星球x和星球y之间有“以太”隧道,可以直接通讯。接下来的一行为一个整数k,表示将遭受攻击的星球的

数目。接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这k个数互不相同,且都在0到n-1的范

围内。

Output

  输出文件的第一行是开始时星球的连通块个数。接下来的N行,每行一个整数,表示经过该次打击后现存星球

的连通块个数。

Sample Input

8 13

0 1

1 6

6 5

5 0

0 6

1 2

2 3

3 4

4 5

7 1

7 2

7 6

3 6

5

1

6

3

5

7

Sample Output

1

1

1

2

3

3

题解:逆向并查集即可,学习了新姿势,不用每次去getfa求联通块数目,只需要每次新加点的时候 num++,然后合并的时候num--即可;

代码:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <bitset>
 6 #include <vector>
 7 #include <queue>
 8 #include <stack>
 9 #include <cmath>
10 #include <list>
11 #include <set>
12 #include <map>
13 #define rep(i,a,b) for(int i = a;i <= b;++ i)
14 #define per(i,a,b) for(int i = a;i >= b;-- i)
15 #define mem(a,b) memset((a),(b),sizeof((a)))
16 #define FIN freopen("in.txt","r",stdin)
17 #define FOUT freopen("out.txt","w",stdout)
18 #define IO ios_base::sync_with_stdio(0),cin.tie(0)
19 #define mid ((l+r)>>1)
20 #define ls (id<<1)
21 #define rs ((id<<1)|1)
22 #define N 400010
23 #define INF 0x3f3f3f3f
24 #define INFF ((1LL<<62)-1)
25 using namespace std;
26 typedef long long LL;
27 typedef pair<int, int> PIR;
28 const double eps = 1e-8;
29
30 int n, m, u, v, t, k[N], f[N], ans[N];
31 vector <int> G[N];
32 bool vis[N];
33 int getfa(int x){
34     return f[x] == x ? x : f[x] = getfa(f[x]);
35 }
36 int main()
37 {
38     //FIN;
39     scanf("%d %d", &n, &m);
40     rep(i, 1, m){
41         scanf("%d %d", &u, &v);
42         G[u].push_back(v);
43         G[v].push_back(u);
44     }
45     scanf("%d", &t);
46     rep(i, 1, t)    { scanf("%d", &k[i]); vis[k[i]] = true; }
47     rep(i, 0, n)    f[i] = i;
48
49     int res = n-t;
50     rep(i, 0, n-1){
51         if(vis[i])  continue;
52         int x = getfa(i);
53         rep(j, 0, (int)G[i].size()-1){
54             if(vis[G[i][j]])    continue;
55             int y = getfa(G[i][j]);
56             if(x != y)  { f[y] = x; res--; }
57         }
58     }
59     /*int res = 0;
60     rep(i, 0, n-1)  { if(!vis[i] && getfa(i) == i)  res++; }*/
61     per(i, t, 0){
62         ans[i] = res++;
63         vis[k[i]] = false;
64         int x = getfa(k[i]);
65         rep(j, 0, (int)G[k[i]].size()-1){
66             if(vis[G[k[i]][j]]) continue;
67             int y = getfa(G[k[i]][j]);
68             if(x != y){
69                 f[y] = x;
70                 res--;
71             }
72         }
73     }
74     rep(i, 0, t)    printf("%d\n", ans[i]);
75     return 0;
76 }

时间: 2024-08-08 18:17:14

BZOJ - 1015【并查集】的相关文章

BZOJ 1015 并查集+离线倒序

统计块个数写错了调了好久啊,BZOJ1696的弱化版本. 1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 #include <map> 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pa pair<int,int>

bzoj 1171 并查集优化顺序枚举 | 线段树套单调队列

详见vfleaking在discuss里的题解. 收获: 当我们要顺序枚举一个序列,并且跳过某些元素,那么我们可以用并查集将要跳过的元素合并到一起,这样当一长串元素需要跳过时,可以O(1)跳过. 暴力: 1 /************************************************************** 2 Problem: 1171 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:1908 ms 7

BZOJ 1050并查集+贪心

1050: [HAOI2006]旅行comf Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3333  Solved: 1851[Submit][Status][Discuss] Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求 一条路径,使得路径上最大边和最小边的比值最小.如果S和T之间没有路径,输出"IMPOSSIB

【BZOJ】1015: [JSOI2008]星球大战starwar(并查集)

http://www.lydsy.com/JudgeOnline/problem.php?id=1015 看了题解的囧T_T,一开始以为是求割点,但是想到割点不能统计.... 这题用并查集,思想很巧妙. 我们按照逆序建图,也就是从最后一个毁了的星球之后建图.然后从后往前走. 那么怎么统计联通块呢?很简单,我们开个数组记录没有被破坏的星球,然后每一次都将一个星球拿去访问他连接的其他点,看他们是否在一个联通块,如果不是,那么就将连接的那个点加入自己的联通块. #include <cstdio> #

BZOJ 1015 星球大战 并查集+离线

这道题说来真是艰辛,从一开始的RE,到RE,到刚刚的WA,再到AC. 这只能说明我进步的历程,还有我需要不断的加强努力.这道题思路不难,从很久前在黑书中并查集一节就能找到解题的踪影,因为并查集只能并,分不了,所以我们就得离线,倒过来写.只不过这道题真的得审好题目,它问的是剩下的星球中有多少个连通分量,不要搞错了.大概就是这个样子了,加油. 1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(int i=

BZOJ 1015: [JSOI2008]星球大战starwar【并查集】

题目可以表述成:给定一个无向图G,每次删除它的一个点和与点相关的边集,每次询问该操作后图G的连通度(连通分量的个数).和上一题一样都是考察逆向思维,虽然删除点的做法不会,但是每次加点后询问连通度却是并查集的经典用法,所以答案可以逆过来推,具体做的时候每次加入一个点,将所有和这个点有边相连的点集合并,然后输出当前有多少个集合.细节部分需要注意的是由于点的数量十分庞大,邻接表是十分有必要的 #include<iostream> #include<cstdio> #include <

BZOJ 1050 旅行comf(枚举最小边-并查集)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1050 题意:给出一个带权图.求一条s到t的路径使得这条路径上最大最小边的比值最小? 思路:将边排序.枚举最小边,然后将边一个一个插到并查集里,s和t联通时计算更新答案. struct node { int u,v,w; void get() { RD(u,v,w); } }; int cmp(node a,node b) { return a.w<b.w; } int n,m,s,t;

BZOJ 3038 上帝造题的七分钟2 (并查集+树状数组)

题解:同 BZOJ 3211 花神游历各国,需要注意的是需要开long long,还有左右节点需要注意一下. #include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL a[100005],c[100005]; int f[100005],n,m,op,l,r,t; int s

BZOJ 3562: [SHOI2014]神奇化合物 并查集+dfs

点击打开链接 注意到20w条边,但是询问只有1w,所以有很多边是从头到尾不变的. 首先离线处理,将从未删除的边缩点,缩点后的图的点数不会超过2w,对于每一次add或者delete,直接dfs看是否能从a走到b,然后维护一个ans. 数据不强,不然这种复杂度起码要跑10s.. #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> using namespace st