codeforces-div1-286-B
来源:互联网 发布:linux mmap fd 编辑:程序博客网 时间:2024/05/16 18:37
题目链接 Mr. Kitayuta's Technology
直接说解法:
先把每个有向边当成无向边, 把有变相连接的点当成一个集合里面的, 然后依次处理每个集合(求每个集合就用并查集).
接下来以此求解每个集合, 每个集合就是一个有向图G, 点就是集合内节点, 边就是题目中的 important transportation, 并且这个图G是联通的(在把边当成无向的情况下).
求解的时候分两种情况, 这个有向图G无环 和 有环
1. 无环
结论: 假设这个无环图G有cnt个节点, 那么需要cnt - 1条边.
证明:
(1) cnt - 1条边足够
我们先把这个无环图拓扑排序, 假设排完后节点顺序为 a0, a1, a2 ... a(cnt-1), 其中a0的入度为0
那么我们这样构建一个图, a0->a1->a2->a3...->a(cnt-2)->a(cnt-1), 对于这个图, 肯定满足条件, 所以cnt - 1条边足够
(2) cnt - 1条边是最小的边数
假设存在一个更优的解 ans(ans < cnt-1), 由于现在有cnt个点, 而只有ans条边, 所以不管怎么用这ans个边来构造, 这个图肯定是不联通的(边无向的情况下).
假设现在用ans构造后的图分为了A, B两大块, 其中A没有到B的边且B没有到A的边.
由于原图G本身是联通的, 因此A中肯定应该有边要求到B 或者 B中肯定应该有边要求到A.
所以ans构造出的图不会满足原图G的要求, 所以cnt - 1是满足条件最小边数.
2. 有环
结论: 假设这个有环图G有cnt个节点, 那么需要cnt条边
证明:
(1) cnt 条边足够
直接把这cnt个节点组成一个大的环, 环中的节点两两可以相 互到达, 肯定满足条件, 所以cnt条边足够.
(2) cnt 条边是最小的
首先对于任何一个 ans (ans < cnt-1)是不可能的, 因为图都不联通了, 理由和无环的情况一样.
然后假设 ans (ans = cnt-1)是一个更优解.
在保证联通的情况下, 用ans个边和cnt个点来构造一个图, 只能构造出一个树, 注意到边是有向的, 所以构造出来的树不可能有环.
而原图G是有环的, 即存在两个节点a, b, 要求a能到b, b能到a.
所以这个边有向的树不可能满足a和b的条件, 所以cnt条边是满足条件的最小边数.
下面是代码
#include <iostream>#include <string.h>#include <vector>#include <queue>using namespace std;#define FOR(i, j, k) for(int i=(j);i<=(k);i++)#define REP(i, n) for(int i=0;i<(n);i++)#define mst(x, y) memset(x, y, sizeof(x));int n, m;int fa[100009];int find_(int u){return u==fa[u]?u:fa[u]=find_(fa[u]);}void merge_(int u, int v){fa[find_(u)] = find_(v);}vector <int> edge[100008];vector <int> grp[100009];int cnt[100008], in[100009];queue <int> q;int solve(int id){ while(!q.empty()) q.pop(); REP(i, grp[id].size()) if(in[grp[id][i]] == 0) q.push(grp[id][i]); int cntt = 0; while(!q.empty()){ cntt ++; int u = q.front(); q.pop(); REP(i, edge[u].size()){ int v = edge[u][i]; if(--in[v] == 0) q.push(v); } } return cntt == cnt[id]? cnt[id]-1 : cnt[id];}int main(){ cin>>n>>m; FOR(i, 1, n) fa[i] = i; mst(in, 0); REP(i, m){ int u, v; cin>>u>>v; edge[u].push_back(v); merge_(u, v); in[v] ++; } mst(cnt, 0); FOR(i, 1, n) grp[find_(i)].push_back(i), cnt[find_(i)] ++; int ans = 0; FOR(i, 1, n) if(grp[i].size()) ans += solve(i); cout<<ans<<endl; return 0;}
- codeforces-div1-286-B
- codeforces 228(div1) B
- CodeForces 177(div1)B
- codeforces round 176Div1 B
- codeforces-div1-286-D
- B. Maximum Value(Codeforces Round #276(div1)
- CodeForces #315 (div1) B.Symmetric and Transitive
- CodeForces #318 (div1) B.Bear and Blocks
- Codeforces #345 div1 B. Image Preview 二分
- codeforces #334 div1 B 603B Moodular Arithmetic(数论)
- Codeforces Round #228 Div1 B Fox and Minimal path
- CodeForces Round #305 (div1) B. Mike and Feet
- codeforces/#305 Div1/547/B Mike and Feet
- Codeforces Round #319 (Div. 1) div1 A B C
- Codeforces #349 div1 B. World Tour 模拟 技巧
- Codeforces Round #333(div1.b/div2.d) / problem 601b - 单调栈
- codeforces 148 div1
- codeforces 150 div1
- 聊聊什么是创业
- 几个 cocos2d 游戏开发辅助工具
- 集群临时重启
- xcode6新建empty工程
- ssh使用公钥登录客户端设置
- codeforces-div1-286-B
- 下拉刷新 上拉加载 修改步骤记录
- mvc3安装 SetupResources.dll missing from 2052 directory 错误解决方法
- Log4j在Java中的使用
- 对象转array
- 程序员需要有多懒 ?- cocos2d-x 数学函数、常用宏粗整理 - by Glede
- Max Sum 贪心
- select函数的使用
- Spring中的事务控制