\(\color{#0066ff}{ 题目描述 }\)

给定一个有向图,改变其中某些边的方向,它将成为一个有向无环图。

现在求一个改变边方向的方案,使得所选边边权的最大值最小。

\(\color{#0066ff}{输入格式}\)

点数n,边数m,接下来是m条有向边

\(\color{#0066ff}{输出格式}\)

输出一个最大值,一个k

接下来一行k个数,表示那些边需要反向

\(\color{#0066ff}{输入样例}\)

  1. 5 6
  2. 2 1 1
  3. 5 2 6
  4. 2 3 2
  5. 3 4 3
  6. 4 5 5
  7. 1 5 4
  8. 5 7
  9. 2 1 5
  10. 3 2 3
  11. 1 3 3
  12. 2 4 1
  13. 4 3 5
  14. 5 4 1
  15. 1 5 3

\(\color{#0066ff}{输出样例}\)

  1. 2 2
  2. 1 3
  3. 3 3
  4. 3 4 7

\(\color{#0066ff}{数据范围与提示}\)

\(2 \leq n \leq 100000\), \(1 \leq m \leq 100000\)

\(\color{#0066ff}{ 题解 }\)

根据题目,显然要二分答案

考虑二分答案之后怎么做

对于比mid大的边,我们肯定是不能改变方向的

于是直接加入图中

然后只需看看有没有环就行了,因为比mid小的边我们可以任意更改

可以用拓扑排序做

因为它只让最大值最小,并没有说改变边的数量最小,所以小的边随便改

现在考虑输出方案

我们在拓扑排序的时候记一下每个点的拓扑序

考虑一条边x到y,如果x的拓扑序大于y,显然可能成环(不是一定成环)

但是如果x的拓扑序小于y,一定不会成环

题目有不限制改边数量,我们就将其反向即可

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. LL in() {
  4. char ch; LL x = 0, f = 1;
  5. while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
  6. for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
  7. return x * f;
  8. }
  9. const int maxn = 1e5 + 10;
  10. struct node {
  11. int x, y, z, id;
  12. friend bool operator < (const node &a, const node &b) {
  13. return a.z < b.z;
  14. }
  15. }e[maxn];
  16. struct E {
  17. int to;
  18. E *nxt;
  19. E(int to = 0, E *nxt = NULL): to(to), nxt(nxt) {}
  20. }pool[maxn], *tail;
  21. int du[maxn], top[maxn];
  22. bool vis[maxn];
  23. int n, m;
  24. E *head[maxn];
  25. void add(int from, int to) {
  26. head[from] = new E(to, head[from]);
  27. }
  28. bool ok(int mid) {
  29. std::queue<int> q;
  30. int cnt = 0;
  31. tail = pool;
  32. for(int i = 1; i <= n; i++) du[i] = 0, head[i] = NULL, top[i] = 0;
  33. for(int i = 1; i <= m; i++) vis[i] = false;
  34. for(int i = m; i >= 1; i--) {
  35. if(e[i].z <= mid) break;
  36. add(e[i].x, e[i].y);
  37. du[e[i].y]++;
  38. }
  39. for(int i = 1; i <= n; i++) if(!du[i]) q.push(i);
  40. while(!q.empty()) {
  41. int tp = q.front(); q.pop();
  42. top[tp] = ++cnt;
  43. for(E *i = head[tp]; i; i = i->nxt) {
  44. du[i->to]--;
  45. if(!du[i->to]) q.push(i->to);
  46. }
  47. }
  48. if(cnt != n) return false;
  49. for(int i = 1; i <= m; i++) {
  50. if(e[i].z > mid) break;
  51. if(top[e[i].x] > top[e[i].y]) vis[e[i].id] = true;
  52. }
  53. return true;
  54. }
  55. int main() {
  56. n = in(), m = in();
  57. for(int i = 1; i <= m; i++) e[i].x = in(), e[i].y = in(), e[i].z = in(), e[i].id = i;
  58. std::sort(e + 1, e + m + 1);
  59. int l = 0, r = 1e9;
  60. int ans = 0;
  61. while(l <= r) {
  62. int mid = (l + r) >> 1;
  63. if(ok(mid)) ans = mid, r = mid - 1;
  64. else l = mid + 1;
  65. }
  66. ok(ans);
  67. int tot = 0;
  68. for(int i = 1; i <= m; i++) if(vis[i]) tot++;
  69. printf("%d %d\n", ans, tot);
  70. for(int i = 1; i <= m; i++) if(vis[i]) printf("%d ", i);
  71. return 0;
  72. }

CF1100E Andrew and Taxi 二分答案+拓扑排序的更多相关文章

  1. CF1100E Andrew and Taxi

    题目地址:CF1100E Andrew and Taxi 二分,每次取到一个 \(mid\) ,只保留长度 \(>mid\) 的边 dfs判环,若有环,说明 \(ans>mid\) ,否则 ...

  2. bzoj5280/luogu4376 MilkingOrder (二分答案+拓扑序)

    二分答案建图,然后判环,就可以了. 字典序输出的话,只要做拓扑序的时候用优先队列来维护就可以了. (其实判环也可以用拓扑序...) #include<cstdio> #include< ...

  3. CF-1100 E Andrew and Taxi

    CF-1100E Andrew and Taxi https://codeforces.com/contest/1100/problem/E 知识点: 二分 判断图中是否有环 题意: 一个有向图,每边 ...

  4. CF 1100E Andrew and Taxi(二分答案)

    E. Andrew and Taxi time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  5. E. Andrew and Taxi(二分+拓扑判环)

    题目链接:http://codeforces.com/contest/1100/problem/E 题目大意:给你n和m,n代表有n个城市,m代表有m条边,然后m行输入三个数,起点,终点,花费.,每一 ...

  6. CF #CROC 2016 - Elimination Round D. Robot Rapping Results Report 二分+拓扑排序

    题目链接:http://codeforces.com/contest/655/problem/D 大意是给若干对偏序,问最少需要前多少对关系,可以确定所有的大小关系. 解法是二分答案,利用拓扑排序看是 ...

  7. 【CF645D】 Robot Rapping Results Report(拓扑排序,二分)

    题意:有一张N点M边的有向图,求最小的K使根据前K条边就能够确定图是否有唯一的拓扑序, 若没有唯一拓扑序输出-1 思路:二分答案再拓扑排序,以入度为0的节点作为新的一层,若某一层的节点个数<&g ...

  8. CROC 2016 - Elimination Round (Rated Unofficial Edition) D. Robot Rapping Results Report 拓扑排序+二分

    题目链接: http://www.codeforces.com/contest/655/problem/D 题意: 题目是要求前k个场次就能确定唯一的拓扑序,求满足条件的最小k. 题解: 二分k的取值 ...

  9. codeforces 645 D. Robot Rapping Results Report 二分+拓扑排序

    题目链接 我们可以发现, 这是一个很明显的二分+拓扑排序.... 如何判断根据当前的点, 是否能构造出来一个唯一的拓扑序列呢. 如果有的点没有出现, 那么一定不满足. 如果在加进队列的时候, 同时加了 ...

随机推荐

  1. Cypress USB3014 controlEndPoint 使用事项

    control endpoint 发送,接收数据 返回fasle , lastError = 997, 抓包查看 Control Transfer (UP) XXXXXXXXX 1. Device: ...

  2. MFRC522模块开发笔记

    Write_to_Card(-)和Read_from_Card(-)可谓是所有函数的终点,而SPIWriteByte(-)则是最底层对MFRC522模块进行操作的函数,所有函数都是为了Write_to ...

  3. 初识python notes

    python数据类型 数字 字符串 列表 元祖 字典 1.为什么要编程 编程的目的是解放人力,这就需要人通过编写程序的方式计算机代替人去自动干活 2.什么是编程语言 编程语言就是人与计算机之间沟通的介 ...

  4. jdbcTemplate学习(三)

    上一节讲的查询方法,映射结果集为对象时,需要一个个set属性值,比较麻烦,下面讲解使用BeanPropertyRowMapper来将查询结果简单映射成对象: 使用Spring的JdbcTemplate ...

  5. 使用AJAX异步提交表单的几种方式

    方式一 手工收集所有的用户输入,封装为大的“k1=v1&k2=v2…”键值对形式,使用$.post(url, data,fn)把数据提交给服务器 $.ajax({ type:'post', u ...

  6. MyBatis总结七:动态sql和sql片段

    开发中,sql拼接很常见,所以说一下动态sql: 1 if 2 chose,when,otherwise 3 where,set 4 foreach 用法解析(现有一张users表 内有id user ...

  7. shell直接退出后 后台进程关闭的原因和对处

    在linux上进行测试时发现启动后台进程后,如果使用exit退出登录shell,shell退出后后台进程还是能够正常运行,但如果直接关闭登陆的窗口(如直接关掉xshell),那后台进程就会一起终了.都 ...

  8. Codeforces 719E (线段树教做人系列) 线段树维护矩阵

    题面简洁明了,一看就懂 做了这个题之后,才知道怎么用线段树维护递推式.递推式的递推过程可以看作两个矩阵相乘,假设矩阵A是初始值矩阵,矩阵B是变换矩阵,求第n项相当于把矩阵B乘了n - 1次. 那么我们 ...

  9. POJ 1151 扫描线 线段树

    题意:给定平面直角坐标系中的N个矩形,求它们的面积并. 题解:建立一个四元组(x,y1,y2,k).(假设y1<y2)用来储存每一条线,将每一条线按x坐标排序.记录所有的y坐标以后排序离散化.离 ...

  10. 利用General框架进行三层架构开发

    三层架构是企业信息管理系统中一种比较流行的架构方式,如大家所知,三层架构将信息系统分为数据访问层(DAL).业务逻辑层(BLL).界面表示层(UI)三部分,三层架构的好处是根据系统中代码所处的层次将系 ...