CF1100E Andrew and Taxi 题解
2021/11/1 23:42:11
本文主要是介绍CF1100E Andrew and Taxi 题解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Description
洛谷传送门
Solution
看到最大值最小这样的字眼,自然想到二分答案。
我们二分所选边权的最大权值,那么比这个值大的边都不能反向,小于等于它的边都可以选择反向。
设当前二分到的权值为 \(mid\),我们不用去管小于等于 \(mid\) 的边(总有方法让它形成不了环),所以我们把大于 \(mid\) 的边全都加进去,利用拓扑排序来判断是否有负环。
具体方法:把进队的点的个数统计出来为 \(sum\),判断是否等于 \(n\)。
-
若 \(sum < n\),有环。
-
若 \(sum = n\),无环。
符合条件的情况下我们就该找哪些边需要反向了。
由于题目中并没有最小化反向边的数量之类的要求,所以我们可以把所有会造成环的边全部反向。
那么有哪些边会形成环呢?
还是拓扑排序,当我们对大于 \(mid\) 的边所形成的图进行拓扑排序时,记录一下它在拓扑序中的位置,即拓扑序。
假设有 \(x\),\(y\) 两点,且拓扑序 \(dfn_y < dfn_x\),此时如果有一条从 \(x\) 连到 \(y\) 的边,那么这条边就会形成负环,就要给它反向。
所以在合法的 \(mid\) 下遍历一遍所有的边,判一下拓扑序即可,注意边权要小于等于 \(mid\)。
Code
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <cstring> #include <queue> using namespace std; const int N = 1e5 + 10; struct node{ int u, v, w, nxt; }edge[N]; int head[N], tot; int n, m, tim; bool vis[N], t[N]; int in[N], dfn[N]; int ans[N], cnt; inline void add(int x, int y, int z){ edge[++tot] = (node){x, y, z, head[x]}; head[x] = tot; } inline int topo(int now){ int sum = 0; queue <int> q; for(int i = 1; i <= n; ++i) if(!in[i]) q.push(i); while(!q.empty()){ int x = q.front(); q.pop(); sum++, dfn[x] = ++tim; for(int i = head[x]; i; i = edge[i].nxt){ int y = edge[i].v; if(edge[i].w <= now) continue; if(!(--in[y])) q.push(y); } } return sum >= n; } inline bool check(int now){ memset(in, 0, sizeof(in)); memset(dfn, 0, sizeof(dfn)); tim = 0; for(int i = 1; i <= m; ++i) if(edge[i].w > now) in[edge[i].v]++; if(!topo(now)) return 0; cnt = 0; for(int i = 1; i <= n; ++i) if(!dfn[i]) dfn[i] = ++tim; for(int i = 1; i <= m; ++i){ int u = edge[i].u, v = edge[i].v; if(edge[i].w <= now && dfn[u] > dfn[v]) ans[++cnt] = i; } return 1; } int main(){ int l = 0, r = 0, maxs = 0; scanf("%d%d", &n, &m); for(int i = 1, u, v, w; i <= m; ++i){ scanf("%d%d%d", &u, &v, &w); add(u, v, w); r = max(r, w); } while(l <= r){ int mid = (l + r) >> 1; if(check(mid)) maxs = mid, r = mid - 1; else l = mid + 1; } printf("%d %d\n", maxs, cnt); sort(ans + 1, ans + 1 + cnt); for(int i = 1; i <= cnt; ++i) printf("%d ", ans[i]); puts(""); return 0; }
End
这篇关于CF1100E Andrew and Taxi 题解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享