noip2019集训测试赛(二十一)Problem B: 红蓝树

noip2019集训测试赛(二十一)Problem B: 红蓝树

Description

有一棵N个点,顶点标号为1N的树。N1条边中的第i条边连接顶点ai和bi
每条边在初始时被染成蓝色。高桥君将进行N1次操作,来把这棵蓝色的树变成红色的树。
* 选一条仅包含蓝色边的简单路径,并删除这些边中的一条。
* 然后在路径的两个端点中间连一条红色的边。
他的目标是,对于每一个i,都有一条红色的边连接cidi
现在请你判断是否可能达成他的目标。

Input

题目数据按一下格式从标准输入输出输入:

NN
ab1

aNbN−1
cd1

cNdN−1

Output

如果目标可以达成,输出`YES`,否则输出`NO`。

Sample Input

sample input 1:
3
1 2
2 3
1 3
3 2
sample input 2:
5
1 2
2 3
3 4
4 5
3 4
2 4
1 4
1 5
sample input 3:
6
1 2
3 5
4 6
1 6
5 1
5 3
1 4
2 6
4 3
5 6

Sample Output

sample output 1:
YES
sample output 2:
YES
sample output 3:
NO

HINT

样例 1 :

目标可以达成:
* 首先,选择连接顶点133的边,并移除12之间的蓝色边,并在13之间生成一条红色边;
* 然后,选择连接顶点23的边,并删去23之间的蓝色边,并在23之间生成一条红色边。

2N105
1ai,bi,ci,diN
aibi
cidi
* 输入的两个图都是树。

解析:

如果可以做到将红树变为蓝树,那么在改变完N-2条边后,最后的没有连上一条蓝边必与最后一条红边重合

那么这一条重合的边可以在以前所有的连边中任意使用

那么我们可以对于原树中每一条既是红的又是蓝的边的两个端点缩成一个点(删去原本连接他们的边,再把其中一个点的所有边转到另一个点上,用启发式合并来缩点)

如果最终整棵树能够缩成一个点,那么就是YES,否则就是NO

启发式合并的总耗时是O(nlogn)

所以整个算法的时间复杂度是O(nlogn)

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
struct data{
    int x,y;
}t[200001];
int n,m,f[200001],x,y,cnt;
set<int> v[200001];
int fa(int a){
    if(f[a]!=a)f[a]=fa(f[a]);
    return f[a];
}
void ins(int a,int b){
    set<int>::iterator id1=v[a].find(b),id2=v[b].find(a);
    if(id1==v[a].end()){
        v[a].insert(b);
        v[b].insert(a);
    }else{
        cnt++;
        v[a].erase(b);
        v[b].erase(a);
        t[cnt].x=a;
        t[cnt].y=b;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n*2-2;i++){
        scanf("%d%d",&x,&y);
        ins(x,y);
    }
    for(int i=1;i<=n;i++)f[i]=i;
    while(cnt){
        x=fa(t[cnt].x);
        y=fa(t[cnt].y);
        cnt--;
        if(v[x].size()>v[y].size())swap(x,y);
        f[x]=y;
        for(int i:v[x]){
            v[i].erase(x);
            ins(i,y);
        }
        v[x].clear();
        m++;
    }
    if(m==n-1)printf("YES\n");
    else printf("NO\n");
}

 

posted @ 2019-09-19 13:31  ez_suyiheng  阅读(847)  评论(0编辑  收藏  举报