Codeforces 605B 构造

Codeforces 605B
题目链接:
http://codeforces.com/problemset/problem/605/B
题意:
给一个图n个点(<=1e5)和m条边(<=1e5),其中有一些边是这个图最小生成树的边。
问此图是否合法。不合法输出-1(包括最小生成树不满足),合法输出一种给每条边分配结点的方案。
思路:
构造题。主要流程是先按照原图生成一个最小生成树然后往树上加边。
试过一种构造 1 - 2 - 3 - 4 - … - n的方法形成最小生成树,但是每次新边应该加在那个地方不好讨论。
然后构造了这样一颗最小生成树:1-2,1-3,….,1-n。然后每次新加边的时候,假设选的是u和v,那么只要满足ValOfNow < max(ValOf(1-u), ValOf(1-v))即可。可分最小生成树边和非最小生成树边进行排序,每次非最小生成树边匹配掉最小的一条树边即可。
那么问题就变成了如何去枚举两条树边。
树边A从小到大枚举,树边B也从小到大枚举并且小于树边A。这样这组组合中有效参与运算的树边是树边A的值Valof(A).因为A是从小到大枚举的,而非树边也是从小到大枚举,所以非树边每次都匹配掉当前A最小且B最小的那个组合即可。
源码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <set>
using namespace std;
const int MAXN = 100000 + 5;
int n, m;
struct D
{
    int u, v;
    int mark;
    int x, y;
}d[MAXN];
bool cmp(D a, D b)
{
    if(a.v == b.v)  return a.u < b.u;
    return a.v > b.v;
}
bool cmp2(D a, D b){return a.mark < b.mark;}
int num[MAXN];
long long sum[MAXN];
int cnt[MAXN];
set<int>s;
int main()
{
    while(scanf("%d%d", &n, &m) != EOF){
        for(int i = 0 ; i < m ; i++)
            scanf("%d%d", &d[i].u, &d[i].v), d[i].mark = i;
        sort(d, d + m, cmp);
        int ok = 1;
        for(int i = 0 ; i < n - 1 ; i++)
            d[i].x = 1, d[i].y = i + 2;
        for(int i = 1 ; i <= n ; i++)
            cnt[i] = i + 1;
        memset(num, 0, sizeof(num));
        for(int i = 1 ; i <= n ; i++)   num[i] = i - 1;
        int now = 0;
        long long tot = 0;
        int u = 3, v = 2;
        for(int i = n - 1; i < m ; i++){
            while(now < n - 1 && d[i].u >= d[now].u)    tot += num[++now];
//            printf("i = %d, now = %d\n", i, now);
            if(tot <= 0){
                ok = 0;
                break;
            }
            tot--;
            d[i].x = u, d[i].y = v;
            v++;
            if(v >= u){u++, v = 2;}
        }
        if(ok == 0) printf("-1\n");
        else{
            sort(d, d + m, cmp2);
            for(int i = 0 ; i < m ; i++)
                printf("%d %d\n", d[i].x, d[i].y);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值