CodeForces 605 B.Lazy Student(构造)

Description
给出一个n个点m条边的图的每条边的边权和该条边是否是这张图的最小生成树的树边,问满足这些条件的图是否存在,如果存在则输出这张图,否则输出-1
Input
第一行输入两个整数n和m表示点数和边数,之后m行每行两个整数ai和bi分别表示第i条边的边权和第i条边是否是最小生成树的树边(2<=n<=1e5,1<=m<=1e5,n-1<=m<=n*(n-1)/2,1<=ai<=1e9)
Output
如果存在满足条件的图则按输入的边权顺序输出对应的边,否则输出-1
Sample Input
4 5
2 1
3 1
4 0
1 1
5 0
Sample Output
2 4
1 4
3 4
3 1
3 2
Solution
构造题,考虑求MST的过程,一条边u->v被淘汰是因为已经找到的MST集合中u->v已经通过边权更小的边可达了,那么在此题的构造中,我们先用给出的n-1条树边构造出一棵MST,然后看剩下的m-n+1条边是否能够通过合理的放置让其在求MST的过程中被淘汰,为使得这n-1条树边起到充分的作用,我们构造一个星型树,即1点分别连向2,3,…,n点形成一棵树,且1-2,1-3,…,1-n的边权递增,对于非树边,我们降序排,贪心的处理边权较大的边,因为边权较大的边不先处理掉的话一些树边的作用可能会被一些边权小的非树边浪费,每次以pos点为终点,起点s从2开始,pos点需满足边1-pos的权不大于当前要插入的边的权w,这样以来把w作为边权赋给s-pos边在求MST时必然会被1-pos边淘汰,每次s=pos时就令pos=pos-1换个终点,然后起点接着从2开始,如果终点也是2了说明当前处理的非树边无法通过树边淘汰掉了,进而无解。
注意到给树边和非树边分别排好序的主要好处就是用游标法优化了每次找pos的过程,时间复杂度O(mlogm)
Code

#include<cstdio>
#include<algorithm>
using namespace std; 
#define maxn 111111
int n,m;
struct node
{
    int w,flag,id,u,v;
    bool operator<(const node &b)const
    {
        if(flag!=b.flag)return flag>b.flag;
        if(flag)return w<b.w;
        return w>b.w;
    }
}a[maxn];
bool cmp(node a,node b)
{
    return a.id<b.id;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a[i].w,&a[i].flag);
            a[i].id=i;
        }
        sort(a+1,a+m+1);
        for(int i=1;i<n;i++)
            a[i].u=1,a[i].v=i+1;
        int gg=0;
        int pos=n;
        while(pos>=3&&a[pos-1].w>a[n].w)pos--;
        for(int i=n,j=pos,k=2;i<=m;i++)
        {
            if(j==2)
            {
                gg=1;
                break;
            }
            while(a[i].w<a[j-1].w)
            {
                j--,k=2;
                if(j==2)
                {
                    gg=1;
                    break;
                }
            }
            if(k==j)j--,k=2;
            if(j==k)
            {
                gg=1;
                break;
            }
            a[i].u=k++,a[i].v=j;
        }
        if(gg)printf("-1\n");
        else
        {
            sort(a+1,a+m+1,cmp);
            for(int i=1;i<=m;i++)printf("%d %d\n",a[i].u,a[i].v);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值