Atcoder AGC014E:Blue and Red Tree (启发式合并)

传送门

题解:
考虑边分治的过程, 最后一定存在两条既在原树上出现,也在新树上出现的边。

我们一定是最后处理这条边,这样就可以合并这两个点变为子问题处理,直到最后只有一个点为止。

合并用启发式合并即可,时间复杂度 O(nlog2n) O ( n log 2 ⁡ n )

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=1e5+50;

int n, anc[N], sze[N];
struct E {
    int x,y;
    E() {}
    E(int _x,int _y)  {
        x=min(_x,_y);
        y=max(_x,_y);
    }
    friend inline bool operator <(const E &a,const E &b) {return a.x<b.x || (a.x==b.x && a.y<b.y);}
};

set <int> *be[N], *re[N];
set <E> all;
typedef set <int> ::iterator it_si;
typedef set <E> :: iterator it_se;
inline it_si nxt(it_si p) {return ++p;}
inline int ga(int x) {return (anc[x]==x) ? x : (anc[x]=ga(anc[x]));}
inline int inc_b(int x,int y) {
    be[x]->insert(y); be[y]->insert(x);
    if(re[x]->find(y)!=re[x]->end()) all.insert(E(x,y)); 
}
inline int dec_b(int x,int y) {
    be[x]->erase(y); be[y]->erase(x);
    it_se t=all.find(E(x,y));
    if(t!=all.end()) all.erase(t);
}
inline int inc_r(int x,int y) {
    re[x]->insert(y); re[y]->insert(x);
    if(be[x]->find(y)!=be[x]->end()) all.insert(E(x,y));
}
inline int dec_r(int x,int y) {
    re[x]->erase(y); re[y]->erase(x);
    it_se t=all.find(E(x,y));
    if(t!=all.end()) all.erase(t);
}
int main() {
    n=rd();
    for(int i=1;i<=n;i++) sze[i]=1, anc[i]=i, be[i]=new set<int>, re[i]=new set<int>;
    for(int i=1;i<n;i++) {
        int x=rd(), y=rd();
        inc_b(x,y);
    }
    for(int i=1;i<n;i++) {
        int x=rd(), y=rd();
        inc_r(x,y);
    }
    int cnt=0;
    while((all.size())&&(++cnt)) {
        int x=all.begin()->x, y=all.begin()->y;
        all.erase(all.begin());
        if(sze[x] < sze[y]) swap(x,y);
        sze[x]+=sze[y]; dec_b(x,y); dec_r(x,y);
        for(it_si it=be[y]->begin(); it!=be[y]->end(); ) {
            int t=*it; it=nxt(it);
            dec_b(y,t); 
            inc_b(x,t);
        }
        for(it_si it=re[y]->begin(); it!=re[y]->end(); ) {
            int t=*it; it=nxt(it);
            dec_r(y,t); 
            inc_r(x,t); 
        }
        delete be[y]; delete re[y];
    }
    puts((cnt==n-1)?"YES":"NO");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值