【NOIP模拟】树

题面

这道题目非常简单,由于是水题信心赛嘛,我就不多废话了。给定一棵树,每一条边连接着树上的两个点集 ,我们让这条边的魔法值为二元组 表示这两个集合中的数的最大值 (你可以认为,就是点编号的最大值)。

好像是很简单的树p呢~
那就请你来做这道题吧!欢乐送分
不过,在比赛开始前 秒,我们一不小心搞丢了这道题的输入数据。 但是,万幸的是,输出数据还在。我们请
你根据输出数据恢复这棵可怜的树

分析

又是一道构造,醉了。

因为前面那道据说送分但是无人AC让我没时间想这个题,直接输出NO走人,25分到手(血赚,前面那个题输NO只有5分23333)

其实血亏,私以为这道构造比第一道简单

好吧,还是那句话 构造题==钻空子

题让你构造树就真的构造树啊!!构造链啊!!(其实也有构造菊花图的)

首先你必须发现,给出的一组点对中,其中有一个点必然是n(显然),如果不是n,就是无答案

而我们用每个点对去除n后剩的数字构成一个序列。从小到大处理这个序列,查到每个在序列中的元素。

当前查到在序列的元素有多个,就把不在序列里且比这个元素小的点和这个元素连边。

因为是从小到大在连边,所以比前一个点大但是比当前元素小的左右两端点不会改变。

最后再连上n这个点就OK了

代码

#include<bits/stdc++.h>
using namespace std;
#define N 5000
int a,b,n,cnt,vis[N];
struct email{int u,v;}e[N*4];
inline void add(int u,int v){e[++cnt].u=u,e[cnt].v=v;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        if(a>b)swap(a,b);
        if(b!=n||a==b){puts("NO");return 0;}
        vis[a]++;
    }
    for(int i=1;i<n;i++)
    {
        if(vis[i]>1)
        {
            int last=i;
            for(int j=1;j<i;j++)
                if(!vis[j])
                {
                    vis[j]++;vis[i]--;
                    add(j,last);last=j;
                    if(vis[i]==1)break;
                }
            if(vis[i]==1)add(last,n);
            else {puts("NO");return 0;}
        }
        else if(vis[i]==1)add(i,n);
    }
    puts("YES");
    for(int i=1;i<=cnt;i++)printf("%d %d\n",e[i].u,e[i].v);
    return 0;
}

 

posted @ 2018-10-31 21:29  WJEMail  阅读(213)  评论(0编辑  收藏  举报