zoukankan      html  css  js  c++  java
  • 【bzoj4998】星球联盟 LCT+并查集

    题目描述

    在遥远的S星系中一共有N个星球,编号为1…N。其中的一些星球决定组成联盟,以方便相互间的交流。但是,组成联盟的首要条件就是交通条件。初始时,在这N个星球间有M条太空隧道。每条太空隧道连接两个星球,使得它们能够相互到达。若两个星球属于同一个联盟,则必须存在一条环形线路经过这两个星球,即两个星球间存在两条没有公共隧道的路径。为了壮大联盟的队伍,这些星球将建设P条新的太空隧道。这P条新隧道将按顺序依次建成。一条新轨道建成后,可能会使一些星球属于同一个联盟。你的任务是计算出,在一条新隧道建设完毕后,判断这条新轨道连接的两个星球是否属于同一个联盟,如果属于同一个联盟就计算出这个联盟中有多少个星球。

    输入

    第1行三个整数N,M和P,分别表示总星球数,初始时太空隧道的数目和即将建设的轨道数目。
    第2至第M+1行,每行两个整数,表示初始时的每条太空隧道连接的两个星球编号。
    第M+2行至第M+P+1行,每行两个整数,表示新建的太空隧道连接的两个星球编号。
    这些太空隧道按照输入的顺序依次建成。
    1≤N,M,P≤200000

    输出

    输出共P行。
    如果这条新的太空隧道连接的两个星球属于同一个联盟,就输出一个整数,表示这两个星球所在联盟的星球数。
    如果这条新的太空隧道连接的两个星球不属于同一个联盟,就输出"No"(不含引号)。

    样例输入

    5 3 4
    1 2
    4 3
    4 5
    2 3
    1 3
    4 5
    2 4

    样例输出

    No
    3
    2
    5


    题解

    LCT+并查集,【bzoj2959】长跑 的简化版。

    由于只有加边没有删边,因此可以使用LCT维护连通关系,如果加入的一条边属于同一个连通块内,那么将他们之间的点缩成一个点。使用并查集维护连通关系和属于的点。

    注意每次找fa时都需要find一遍,以找到真正的fa。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 200010
    using namespace std;
    int bl[N] , fa[N] , c[2][N] , w[N] , rev[N] , con[N];
    int find(int x)
    {
    	return x == bl[x] ? x : bl[x] = find(bl[x]);
    }
    int getcon(int x)
    {
    	return x == con[x] ? x : con[x] = getcon(con[x]);
    }
    void pushdown(int x)
    {
    	if(rev[x])
    	{
    		int l = c[0][x] , r = c[1][x];
    		swap(c[0][l] , c[1][l]) , swap(c[0][r] , c[1][r]);
    		rev[l] ^= 1 , rev[r] ^= 1 , rev[x] = 0;
    	}
    }
    bool isroot(int x)
    {
    	return c[0][find(fa[x])] != x && c[1][find(fa[x])] != x;
    }
    void update(int x)
    {
    	if(!isroot(x)) update(find(fa[x]));
    	pushdown(x);
    }
    void rotate(int x)
    {
    	int y = find(fa[x]) , z = find(fa[y]) , l = (c[1][y] == x) , r = l ^ 1;
    	if(!isroot(y)) c[c[1][z] == y][z] = x;
    	fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
    }
    void splay(int x)
    {
    	int y , z;
    	update(x);
    	while(!isroot(x))
    	{
    		y = find(fa[x]) , z = find(fa[y]);
    		if(!isroot(y))
    		{
    			if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);
    			else rotate(y);
    		}
    		rotate(x);
    	}
    }
    void access(int x)
    {
    	int t = 0;
    	while(x) splay(x) , c[1][x] = t , t = x , x = find(fa[x]);
    }
    void makeroot(int x)
    {
    	access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] ^= 1;
    }
    void link(int x , int y)
    {
    	makeroot(x) , fa[x] = y , con[getcon(x)] = getcon(y);
    }
    void dfs(int x , int y)
    {
    	if(!x) return;
    	bl[x] = y , w[y] += w[x];
    	dfs(c[0][x] , y) , dfs(c[1][x] , y);
    }
    int add(int x , int y)
    {
    	x = find(x) , y = find(y);
    	if(x == y) return w[x];
    	else if(getcon(x) != getcon(y))
    	{
    		link(x , y);
    		return -1;
    	}
    	makeroot(x) , access(y) , splay(y);
    	dfs(c[0][y] , y);
    	return w[y];
    }
    int main()
    {
    	int n , m , p , i , x , y , t;
    	scanf("%d%d%d" , &n , &m , &p);
    	for(i = 1 ; i <= n ; i ++ ) bl[i] = con[i] = i , w[i] = 1;
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y);
    	for(i = 1 ; i <= p ; i ++ )
    	{
    		scanf("%d%d" , &x , &y) , t = add(x , y);
    		if(~t) printf("%d
    " , t);
    		else puts("No"); 
    	}
    	return 0;
    }
    

      

     

  • 相关阅读:
    fdisk 分区
    fdisk 添加逻辑分区
    centos7 bond0 双网卡配置
    查看centos7启动项
    本地yum源安装docker
    cobbler Ubuntu16.04 安装
    docker-ce-17.03.2 离线安装RPM包
    day14 生成器的进阶
    day13迭代器与生成器
    day12闭包,装饰器
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7413361.html
Copyright © 2011-2022 走看看