您的位置:首页 > 其它

Codeforces 605B 构造

2015-12-11 00:04 375 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: