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;}


0 0