题目链接:http://codeforces.com/problemset/problem/605/B
题目大意:有一个网(加权图),无向,无环,给出你他的顶点个数n(2<=n<=100000)和边的个数m( 1<=m<=100000且n-1<=m<=n*(n-1)/2 ),然后有m行,每行是一个整数和一个bool变量,第一个数表示这个边的权值,第二个数,1表示这条边在这个图的最小生成树(MST)里,0表示不在MST里。求满足条件的每个边连接的两个顶点序号,有多种情况,给出其中一种即可,如果不存在输出-1,存在就输出m行,每行两个数表示这条边所连接的两个顶点(输出顺序要与输入顺序对应)。
解:都有点不太想写,这套题都是构造题0.0,没有完全的构造出来,最后还是看的题解。
假设有两个顶点集U和V,初始时,U中只有1号顶点,V中有其他的顶点。
先将所有的边按升序排序,权值一样的话将属于MST的排到前面,然后从小到大处理每条边。
遇到一个新的属于MST的边就把这条边连接一个V中的顶点,并把这个顶点从V中移除,加到U中,如果是不属于MST的,就让这个边连接U中任意两个未连接的顶点。不合法的情况就是在连接U中任意两个未连接的顶点时没有点可连。
两种构造方法。一,对于MST的里的边就是1-2,1-3,,,1-n,
对于不是MST里的边,假设当前U里有x个顶点,设l和r是连接这条边连接的两个点,那么每次,如果l<r,就作连接,然后l++,否则就是l==r,那就让r++,l=2。非法情况就是r++时,如果r>x,就是非法,输出-1.
二,对于MST里的边就是1-2,2-3,,,(n-1)-n。
对于不是MST里的边,假设当前U里有x个顶点,设l和r是连接这条边连接的两个点,那么每次,如果l < r-1,就作连接,然后l++,否则就是l==r-1,那就让r++,l=1。非法情况就是r++时,如果r>x,就是非法,输出-1.
其实就是构造,反正构造出一种合理的方法就好。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m;
//bool U[100005], V[100005];
int connect[100005][2];
struct edge_unit
{
int k, i, f;
bool operator <(edge_unit a)
{
if (this->k < a.k)
return true;
else if (this->k == a.k)
return this->f>a.f;
else
return false;
}
}edge[100005];
bool work()
{
int l = 2, r = 2, p = 1;
for (int i = 0; i < m; ++i)
{
if (edge[i].f)
{
connect[edge[i].i][0] = 1;
connect[edge[i].i][1] = ++p;
}
else
{
if (l == r)
{
++r; l = 2;
if (r > p)
return 0;
}
connect[edge[i].i][0] = l++;
connect[edge[i].i][1] = r;
}
}
return 1;
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d%d", &n, &m);
int x, flag;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &x, &flag);
edge[i].k = x;
edge[i].f = flag;
edge[i].i = i;
}
sort(edge, edge + m);
if (work())
for (int i = 0; i < m; ++i)
printf("%d %d\n", connect[i][0], connect[i][1]);
else
printf("-1\n");
//system("pause");
//while (1);
return 0;
}