codeforces 1100E 二分 + 拓扑排序

E. Andrew and Taxi

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

Andrew prefers taxi to other means of transport, but recently most taxi drivers have been acting inappropriately. In order to earn more money, taxi drivers started to drive in circles. Roads in Andrew's city are one-way, and people are not necessary able to travel from one part to another, but it pales in comparison to insidious taxi drivers.

The mayor of the city decided to change the direction of certain roads so that the taxi drivers wouldn't be able to increase the cost of the trip endlessly. More formally, if the taxi driver is on a certain crossroads, they wouldn't be able to reach it again if he performs a nonzero trip.

Traffic controllers are needed in order to change the direction the road goes. For every road it is known how many traffic controllers are needed to change the direction of the road to the opposite one. It is allowed to change the directions of roads one by one, meaning that each traffic controller can participate in reversing two or more roads.

You need to calculate the minimum number of traffic controllers that you need to hire to perform the task and the list of the roads that need to be reversed.

Input

The first line contains two integers nn and mm (2≤n≤1000002≤n≤100000, 1≤m≤1000001≤m≤100000) — the number of crossroads and the number of roads in the city, respectively.

Each of the following mm lines contain three integers uiui, vivi and cici (1≤ui,vi≤n1≤ui,vi≤n, 1≤ci≤1091≤ci≤109, ui≠viui≠vi) — the crossroads the road starts at, the crossroads the road ends at and the number of traffic controllers required to reverse this road.

Output

In the first line output two integers the minimal amount of traffic controllers required to complete the task and amount of roads kk which should be reversed. kk should not be minimized.

In the next line output kk integers separated by spaces — numbers of roads, the directions of which should be reversed. The roads are numerated from 11 in the order they are written in the input. If there are many solutions, print any of them.

Examples

input

Copy

5 6
2 1 1
5 2 6
2 3 2
3 4 3
4 5 5
1 5 4

output

Copy

2 2
1 3 

input

Copy

5 7
2 1 5
3 2 3
1 3 3
2 4 1
4 3 5
5 4 1
1 5 3

output

Copy

3 3
3 4 7 

Note

There are two simple cycles in the first example: 1→5→2→11→5→2→1 and 2→3→4→5→22→3→4→5→2. One traffic controller can only reverse the road 2→12→1 and he can't destroy the second cycle by himself. Two traffic controllers can reverse roads 2→12→1 and 2→32→3 which would satisfy the condition.

In the second example one traffic controller can't destroy the cycle 1→3→2→11→3→2→1. With the help of three controllers we can, for example, reverse roads 1→31→3 ,2→42→4, 1→51→5.

题目大意:一个包含n个点的有向图,每条边有一个权值value,反转一条权值为value的边至少需要value个转换器,转换器可以重复使用,现在问至少需要多少个转换器可以是这个有向图无环。

二分转换器的个数x,每次check的方法为,将权值小于x的边删除,若通过拓扑排序发现剩下的子图有环则不成立,否则x成立。最后输出转换的边的方法为,对于小于x的边 (u,v),若u的拓扑序大于v,那么这条边将有可能形成环,那么就要反转它,这样一定不会出现环。

#include <cstdio>
#include <algorithm>
#include <queue>

using namespace std;
const int N = 1e5 + 10;
queue<int> Q;
int rk[N], degree[N], cnt;
struct node {
    int u, v, w;
}edge[N];
vector<int> G[N];
vector<int> res;
int n, m;

int check(int x) {
    cnt = 0;
    for (int i = 1; i <= n; i++) {
        rk[i] = degree[i] = 0;
        G[i].clear();
    }
    while (!Q.empty()) Q.pop();
    for (int i = 1; i <= m; i++) {
        if (edge[i].w > x) {
            G[edge[i].u].push_back(i);
            degree[edge[i].v]++;
        }
    }
    for (int i = 1; i <= n; i++) 
        if (degree[i] == 0) {
            rk[i] = cnt++;
            Q.push(i);
        }
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        for (int i = 0; i < G[u].size(); i++) {
            int num = G[u][i];
            int v = edge[num].v;
            degree[v]--;
            if (degree[v] == 0) {
                Q.push(v);
                rk[v] = cnt++;
            }
        }
    }
    return cnt == n;
}

int main() {
    scanf("%d%d", &n, &m);
    int l = 0, r = 0;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
        r = max(r, edge[i].w);
    }
    int ans = r;
    while (l < r) {
        int m = (l + r) / 2;
        if (check(m)) {
            ans = m;
            r = m;
        }
        else l = m + 1;
    }
    check(ans);
    for (int i = 1; i <= m; i++) if (edge[i].w <= ans && rk[edge[i].u] > rk[edge[i].v]) res.push_back(i);
    printf("%d %d\n", ans, res.size());
    for (int i = 0; i < res.size(); i++) printf("%d ", res[i]);
    printf("\n");
    
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值