Codeforces 606 D Lazy Student【逆最小生成树+思维】好题

D. Lazy Student

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

Student Vladislav came to his programming exam completely unprepared as usual. He got a question about some strange algorithm on a graph — something that will definitely never be useful in real life. He asked a girl sitting next to him to lend him some cheat papers for this questions and found there the following definition:

The minimum spanning tree T of graph G is such a tree that it contains all the vertices of the original graph G, and the sum of the weights of its edges is the minimum possible among all such trees.

Vladislav drew a graph with n vertices and m edges containing no loops and multiple edges. He found one of its minimum spanning trees and then wrote for each edge its weight and whether it is included in the found tree or not. Unfortunately, the piece of paper where the graph was painted is gone and the teacher is getting very angry and demands to see the original graph. Help Vladislav come up with a graph so that the information about the minimum spanning tree remains correct.

Input

The first line of the input contains two integers n and m () — the number of vertices and the number of edges in the graph.

Each of the next m lines describes an edge of the graph and consists of two integers aj and bj (1 ≤ aj ≤ 109, bj = {0, 1}). The first of these numbers is the weight of the edge and the second number is equal to 1 if this edge was included in the minimum spanning tree found by Vladislav, or 0 if it was not.

It is guaranteed that exactly n - 1 number {bj} are equal to one and exactly m - n + 1 of them are equal to zero.

Output

If Vladislav has made a mistake and such graph doesn't exist, print  - 1.

Otherwise print m lines. On the j-th line print a pair of vertices (uj, vj) (1 ≤ uj, vj ≤ n, uj ≠ vj), that should be connected by the j-th edge. The edges are numbered in the same order as in the input. The graph, determined by these edges, must be connected, contain no loops or multiple edges and its edges with bj = 1 must define the minimum spanning tree. In case there are multiple possible solutions, print any of them.

Examples

Input

4 5
2 1
3 1
4 0
1 1
5 0

Output

2 4
1 4
3 4
3 1
3 2

Input

3 3
1 0
2 1
3 1

Output

-1

 

题目大意:

给你n个点,m条无向边,接下来m行,每行表示当前边的信息:边的权值,边是否属于最小生成树(1表示属于0表示不属于)。

问你能否对应每条边构建出来两个顶点,使得最终构成的图满足给出的条件。


思路:


1、首先根据最小生成树的贪心策略可知(克鲁斯卡尔算法),我们先对所有给出的边进行排序。


2、根据克鲁斯卡尔算法过程可知,按照从小到大排序之后的边如果属于最小生成树,其当前边的作用一定是使得两个点连通(而且加上这条边之前这两个点没有连通)。如果不属于最小生成树,其当前边的两个点一定在之前就已经连通了。那么根据这个特性,而且这个题是要输出任意解,那么我们初始化答案变量:ans1=1,ans2=2;如果当前边属于了最小生成树,对应这条边就设定为(1,2),然后ans1++,ans2++,再遇到吓一条属于最小生成树的边之后,这条边就设定为(2,3),然后ans1++,ans2++,依次类推。


3、以上,我们就解决了如果当前边属于最小生成树边的情况的解。那么如果当前遇到的边不属于最小生成树的边我们要如何处理呢?

上边已经说过了,如果按照了从小到大边权值排序之后的这条边如果不属于最小生成树,那么这条边的两个顶点一定是已经连通了的,那么需要设定的边其实也不难想到:

①设定queue【10000】;queue【i】=v,初始化v=i+2。

②那么考虑这样一个情况:

1 1

2 1

3 1

4 0

那么在遇到4 0之前,我们已经在图中加入了三条边:(1,2)(2,3)(3,4),那么这个时候遇到了4 0,这条边不属于最小生成树的边,这个时候我们枚举一层for,从点1开始,一直枚举到ans1-1为止,如果queue【i】<ans2,那么就可以加入图中:(i,queue【i】)这条边。显然,其这条边的两点,一定在之前就已经连通了,加入的这条边,也已经保证了的确不属于最小生成树的边。

如果没有枚举到可行边,那么很可惜,这个时候要输出-1了。

4、思路建立完毕,剩下的内容就是实现代码了。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
struct node
{
    int w,op,pos;
}a[120000];
int cmp(node a,node b)
{
    if(a.w==b.w)return a.op>b.op;
    else return a.w<b.w;
}
queue<int >s[100005];
int output[120000][2];
int n,m;
void init()
{
    for(int i=1;i<=100005;i++)
    {
        s[i].push(i+2);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        memset(output,0,sizeof(output));
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a[i].w,&a[i].op);
            a[i].pos=i;
        }
        sort(a,a+m,cmp);
        int ans1=1;
        int ans2=2;
        int x=1,y=3;
        int flag=0;
        /*
        printf("---------------\n");
        for(int i=0;i<m;i++)
        {
            printf("%d %d\n",a[i].w,a[i].op);
        }
        printf("----------------\n");
        */
        for(int i=0;i<m;i++)
        {
            if(a[i].op==1)
            {
                output[a[i].pos][0]=ans1;
                output[a[i].pos][1]=ans2;
                ans1++;
                ans2++;
            }
            else
            {
                int tmp=0;
                for(int j=0;j<ans1;j++)
                {
                    int u=s[j].front();
                    if(u<=ans2-1)
                    {
                        s[j].pop();
                        tmp=1;
                        output[a[i].pos][0]=j;
                        output[a[i].pos][1]=u;
                        u++;
                        s[j].push(u);
                        break;
                    }
                }
                if(tmp==0)
                {
                    flag=1;
                    break;
                }
            }
        }
        if(flag==1)
        {
            printf("-1\n");
        }
        else
        {
            for(int i=0;i<m;i++)
            {
                printf("%d %d\n",output[i][0],output[i][1]);
            }
        }
    }
}






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值