-
[1508] 火烧赤壁2
- 时间限制: 1000 ms 内存限制: 65535 K
- 问题描述
-
上次出了一道火烧赤壁的题目给当时的新生,也就是你们的上一届学长们做,那么这次,我又想到了另一个想法。
上次的火烧赤壁的题号为1274
这次稍微难一点,我们来个火烧赤壁2吧。
Hungar个人很喜欢曹操这个人,所以这次有机会穿越到三国时代,他想帮助曹操打赢赤壁之战。
但是他去晚了,当他穿越到那的时候发现大火已经在蔓延了,所以他能做的就是马上告诉曹操把已经着火的船的锁链(船与船相连都是靠锁链达到的)给破坏掉使火不会蔓延到附近的船只。
现在告诉你曹操一共有N艘船,M条铁链,船只编号从0开始到n-1。
然后再告诉你依次着火的船只编号,问舍弃那艘船以后,剩下的船只能形成几个连通块(只要是被铁链连在一起的全部船只,就算一共连通块)。
船与船之间可能存在多条锁链。
- 输入
-
输入包括第一行两个整数,N(1 <= N <= 2M)和M(1 <= M <= 200,000)。
接下来M行,每行包括两个整数x和y(x != y),分别表示编号为x和y的船只被一根锁链连起来。
再接下来一个正整数T表示着火船只的数量。
接下来T行,每行包含一个整数z表示被烧船只的编号,编号不会出现一样的,也就是说已经被烧的船只不会再去烧它。
- 输出
-
输出z+1个数,第一行为着火前这些船的连通块数,后z行表示每次依次烧掉一只船后,剩下的连通块数。
- 样例输入
-
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
- 样例输出
-
1 1 1 2 3 3
思路:题目中舍弃的船是有先后次序的,前面的会对后面的造成影响。然后什么是逆序加边?先把所有的连接点对和舍弃的船全存起来,然后把所有不在舍弃的点集合中的点进行合并,比如题目中一开始就只存在0,2,4这三个点,且没有边可以合并(任意边均有至少一个点属于被舍弃的集合)这样完成之后的连通情况记为F。然后可以推出:我最后一次舍弃的是7号,求此时的连通块的做法。求7号时显然前面的点已经全部被舍弃,那么这种情况等价于F;然后往前推求舍弃5的情况,显然此时情况等价于F加上7所在的所有边。然后3、6、1情况以此类推。由此可见逆序可以减小复杂度,不然我顺序每求一次都要把连通情况初始化为F,然后又进行倒序加边。复杂度由N范围决定,数据一大肯定超时。然后此题WA多次,最后几次是由于多输出了一个数,最后因为文字太多看不清用文件重定向才发现是多输出了一个数……
代码:
#include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<cstdio> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> #define INF 0x3f3f3f3f #define MM(x) memset(x,0,sizeof(x)) using namespace std; typedef long long LL; const int N=200010; int pre[2*N]; int ran[2*N]; int vis[2*N]; vector<int>E[N]; vector<int>pos; inline int find(const int &a) { if(a!=pre[a]) return pre[a]=find(pre[a]); return pre[a]; } inline int joint(const int &a,const int &b) { int fa=find(a),fb=find(b); if(fa!=fb) { if(ran[fa]>=ran[fb]) { ran[fa]+=ran[fb]; pre[fb]=fa; ran[fb]=0; } else { ran[fb]+=ran[fa]; pre[fa]=fb; ran[fa]=0; } return 1; } return 0; } inline void init(int n) { for (int i=0; i<n; i++) { pre[i]=i; ran[i]=1; vis[i]=1; E[i].clear(); } pos.clear(); } inline void init2(int n) { for (int i=0; i<n; i++) { pre[i]=i; ran[i]=1; } } int main(void) { int i,j,x,y,z,n,m,t,k; while (~scanf("%d%d",&n,&m)) { init(n); for (i=0; i<m; i++) { scanf("%d%d",&x,&y); E[x].push_back(y); E[y].push_back(x); joint(x,y); } k=0; for (i=0; i<n; i++) { if(ran[i]>=1) k++; } init2(n); k=n; scanf("%d",&t); for (i=0; i<t; i++) { scanf("%d",&z); pos.push_back(z); if(vis[z]==1) { vis[z]=0; k--; } } vector<int>ans; for (i=0; i<n; i++) { if(vis[i]==1) { for (j=0; j<E[i].size(); j++) { if(vis[E[i][j]]) { if(joint(E[i][j],i)) k--; } } } } ans.push_back(k); int cnt=pos.size(),fa,fb; for (i=cnt-1; i>=0; i--) { int v=pos[i]; vis[v]=1; k++; for (j=0; j<E[v].size(); j++) { if(vis[E[v][j]]) { if(joint(E[v][j],v)) k--; } } ans.push_back(k); } cnt=ans.size(); for (i=cnt-1; i>=0; i--) printf("%d ",ans[i]); } return 0; }