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

Posted sovietpower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

以上是关于AGC 014E.Blue and Red Tree(思路 启发式合并)的主要内容,如果未能解决你的问题,请参考以下文章

AtCoder Grand Contest 014 E:Blue and Red Tree

AGC014做题记录

AGC014-F Strange Sorting

[AGC014F]Strange Sorting

AtCoderAGC014

agc045_d Lamps and Buttons