• AGC 014E.Blue and Red Tree(思路 启发式合并)


    题目链接

    (Description)

    给定两棵(n)个点的树,分别是由(n-1)条蓝边和(n-1)条红边组成的树。求(n-1)次操作后,能否把蓝树变成红树。
    每次操作是,选择当前树上一条只由蓝边组成的简单路径(u o v),删掉路径上的任意一条蓝边,然后在路径上任选两个点,在这两个点之间加一条红边。
    (nleq10^5)

    (Solution)

    模拟一下样例二,就比较容易想到:
    考虑能否从红树变回到蓝树。
    我们每次要找到当前树上一条蓝边组成的路径(u o v),设路径上任意一点是(a),不在路径上的一个点是(b)。如果当前(a,b)之间有红边相连,而(b)与路径上某点(c)在蓝树上有边,显然这条红边((a,b))是可以通过删掉((b,c))加入的,也就是现在我们在当前树上删掉边((a,b)),加入边((b,c))。然后继续找蓝边路径继续扩展。
    怎么实现这一过程呢?
    对于整条蓝边组成的路径(u o v),我们不需要管具体有哪些点,只需要知道(b)(u o v)之间既有一条红边,也有一条蓝边(不会有两条红边/蓝边啊,因为每次操作后也是一棵树),然后我们可以删掉这条红边,加入这条蓝边。边具体是哪条也不需要管,我们只需要知道(b)被加入了路径(u o v)
    所以就可以用并查集实现。(b)被合并到路径(u o v)前,要把与(b)相连的所有边连到路径(u o v)(的代表点)上去,可以用启发式合并。
    同时有红边和蓝边与路径相连,就是这条边出现了两次。所以我们把出现两次的边拿出来扩展就好了。
    能扩展(n-1)次则可行,否则不行。

    启发式合并然后用set维护出边,复杂度(O(nlog^2n))。出边可以hash点对((u,v))来维护,少一个(log),懒得写了。。

    //287ms	25472KB
    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() getchar()
    typedef long long LL;
    const int N=1e5+5;
    
    int fa[N];
    std::set<int> st[N];
    std::pair<int,int> q[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    int Find(int x)
    {
    	return x==fa[x]?x:fa[x]=Find(fa[x]);
    }
    
    int main()
    {
    	const int n=read();
    	int h=0,t=0;
    	for(int i=n-1<<1; i; --i)
    	{
    		int u=read(),v=read();
    //		if(u>v) std::swap(u,v);
    		if(!st[u].count(v)) st[u].insert(v), st[v].insert(u);
    		else q[t++]=std::make_pair(u,v);
    	}
    	for(int i=1; i<=n; ++i) fa[i]=i;
    	while(h<t)
    	{
    		int x=Find(q[h].first),y=Find(q[h++].second);
    		if(st[x].size()<st[y].size()) std::swap(x,y);//x<-y
    		for(std::set<int>::iterator it=st[y].begin(); it!=st[y].end(); ++it)
    			if(*it!=x)
    			{
    				int u=x,v=*it;
    				st[v].erase(y);
    //				if(u>v) std::swap(u,v);
    				if(!st[u].count(v)) st[u].insert(v), st[v].insert(u);
    				else q[t++]=std::make_pair(u,v);		
    			}
    		st[x].erase(y), fa[y]=x;
    	}
    	puts(t==n-1?"YES":"NO");
    
    	return 0;
    }
    
  • 相关阅读:
    让windows10的右键菜单既显示传统cmd又显示powershell
    配置php环境的一个nginx.conf
    windows中启动和终止nginx的两个批处理
    WxWidgets笔记
    安装archlinux的linux命令记录
    window中的attrib命令
    docker中i的作用
    airflow中的两个参数
    sqlite数据库中为字段设置默认值为当前时间
    记oracle使用expdp将数据导出到asm报错
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10237791.html
Copyright © 2020-2023  润新知