zoukankan      html  css  js  c++  java
  • NOJ——1508火烧赤壁2(并查集+启发式合并+逆序加边)

    • [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;
    }
  • 相关阅读:
    c++中memset()函数的用法
    not exits用法讲解(以查询选修全部课程的学生为例)
    数据库中冲突可串行化的判断
    如何快速的判断一个数是否是完全平方数
    杭电oj2089c++
    3月7日学习日志
    3月5日 学习日志
    免编译二进制安装mysql5.7
    base64转换为图片以及图片转换为base64码
    Eclipse编辑JS响应慢,复制粘贴卡顿的解决方案
  • 原文地址:https://www.cnblogs.com/Blackops/p/5766346.html
Copyright © 2011-2022 走看看