- vector和邻接表
- 并查集的一个很风骚的技巧
一. vector数组操作
包含vector头文件
声明:vector<type> name;
方法:
- 加入一个元素至最后:vec.push_back(val);
- 清空数组中的所有元素:vec.clear();
- 访问其中元素:vec.at(i);
- vector中元素的个数:vec.size();
- 指定当前vector内元素个数:vec.resize(n); 会保留前n个元素。
邻接表是一种储存图的方式,通常使用链表或者vector可变长数组实现。由表头节点和表节点组成,图中的每一个顶点都在邻接表中为一个表头节点。
邻接表与普通数组储存图的好处在于节约了空间与时间。
如果要遍历一个N个节点的图,数组需要两层for每层N次,事实上一个点并不一定与其它所有点都相邻,进行了无用的判断,还耗费了空间。
如果使用邻接表,使用vector类,可以使速度加快,空间变小,邻接表只储存于表头节点相邻的顶点,因此少了不必要的空间和遍历。
题目:
Hungar个人很喜欢曹操这个人,所以这次有机会穿越到三国时代,他想帮助曹操打赢赤壁之战。
但是他去晚了,当他穿越到那的时候发现大火已经在蔓延了,所以他能做的就是马上告诉曹操把已经着火的船的锁链(船与船相连都是靠锁链达到的)给破坏掉使火不会蔓延到附近的船只。
现在告诉你曹操一共有N艘船,M条铁链,船只编号从0开始到n-1。
然后再告诉你依次着火的船只编号,问舍弃那艘船以后,剩下的船只能形成几个连通块(只要是被铁链连在一起的全部船只,就算一共连通块)。
船与船之间可能存在多条锁链。
Input
输入包括第一行两个整数,N(1 <= N <= 2M)和M(1 <= M <= 200,000)。
接下来M行,每行包括两个整数x和y(x != y),分别表示编号为x和y的船只被一根锁链连起来。
再接下来一个正整数T表示着火船只的数量。
接下来T行,每行包含一个整数z表示被烧船只的编号,编号不会出现一样的,也就是说已经被烧的船只不会再去烧它。
Output
输出z+1个数,第一行为着火前这些船的连通块数,后z行表示每次依次烧掉一只船后,剩下的连通块数。
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 由于题目中顶点的数量高达400000,所以使用邻接表储存图,然而题目要求的操作是每次去掉一个顶点,判断一次联通块数量,可以每次都去掉一个点,进行一次图遍历。复杂度太高,舍弃。技巧:反向操作,从一个已有的并查集中去除一个点非常难,但是加入一个点很简单,倒序将点加入,每次遍历加入的顶点的邻接表。最后倒序输出即为答案。
#include <iostream> #include <stdio.h> #include <string.h> #include <vector> #define MAX 400010 using namespace std; vector<int> vis[MAX]; int ans[MAX]; int rec[MAX]; bool node[MAX]; int fa[MAX]; int N,M; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void init() { memset(node,0,MAX * sizeof(bool)); for (int i = 0; i < N; ++i) fa[i] = i; for(int i = 1 ; i <= MAX ; i++) vis[i].clear(); return ; } int main() { while(~scanf("%d%d",&N,&M)) { init(); int a , b; for (int i = 1; i <= M ; ++i) { scanf("%d%d",&a,&b); vis[a].push_back(b); vis[b].push_back(a); } /////////////////////////////////// int case_num; scanf("%d",&case_num); int nodenum = N; for (int i = case_num - 1; i >= 0; i--) { scanf("%d",&rec[i]); node[rec[i]] = 1; nodenum--; } int key = 0; ans[key] = nodenum; ///////////////////////// for (int i = 0; i < N; ++i) { a = i; for (int j = 0; j < vis[a].size(); ++j) { b = vis[a].at(j); if(node[a] || node[b]) continue; if(find(a) != find(b)) {find(a) < find(b) ? fa[find(b)] = find(a) : fa[find(a)] = find(b);ans[key]--;} } } /////////////////////// for (int i = 0; i < case_num; ++i) { key++; ans[key] = ans[key - 1] + 1; int a = rec[i]; node[a] = 0; for (int j = 0; j < vis[a].size(); ++j) { b = vis[a].at(j); if(node[a] || node[b]) continue; if(find(a) != find(b)) { find(a) < find(b) ? fa[find(b)] = find(a) : fa[find(a)] = find(b); ans[key]--;} } } for(int i = case_num ; i >= 0 ; i--) cout << ans[i] <<endl; } return 0; }