BZOJ 3624: [Apio2008]免费道路

3624: [Apio2008]免费道路

Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 1201  Solved: 469
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

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

Sample Output

3 2 0
4 3 0
5 3 1
1 2 1

HINT

 

Source

 
[Submit][Status][Discuss]

 

哎呀,这题好气啊,明明不难的,就是想不到啊,还是我太蒟蒻了啊。

 

题意要求我们求出一棵生成树,但是这次限制的不是权值大小,而是在一个给定的边集的子集中选出恰好K条边加入生成树,输出任意一种方案即可,SPJ。

 

这个其实很简单,你就先紧着子集里的边加,做Kruskal,求出一棵生成树(也许只能做出一棵生成森林),这是你保证了子集中选出的边>=K(如果小于K那就是无解啦),这时先用子集外的边填补漏洞,把森林补成树(补不成就是无解啦),然后再不断尝试加入子集外的边,显然加入之后会有环,那么替换掉环上的一条子集边即可(如果环上有的话),这个用LCT维护就好了,反正N<=20000是吧。

 

其实上面只是开玩笑的,如果你真的像我一开始这么想,看到Time Limit你就绝望了,23333——手动滑稽

 

正解是先紧着子集外的边加,造一个森林(能是树最好啦),然后用子集内的边补上不连通的位置。如果不能补成树或使用的子集边>K,那么就是无解。那么此时用来填补非子集边无法做到的联通性的边(就是刚才选出来的子集边)是一定要选的(并非必须边,但是一定存在一组可行解包含它们)。那么先选上这些,下面我们一定可以用非子集边把图连成生成树了,但是可能子集边还不到K,那就再随便选几个啦,然后再用非子集填补空缺。

 

  1 #include <cstdio>
  2 
  3 const int mxn = 20005;
  4 const int mxm = 100005;
  5 
  6 int n, m, k;
  7 
  8 struct edge
  9 {
 10     int x, y, c;
 11 }e[mxm];
 12 
 13 int fa[mxn];
 14 
 15 int find(int u)
 16 {
 17     return u == fa[u] ? u : fa[u] = find(fa[u]);
 18 }
 19 
 20 bool vis[mxm];
 21 
 22 signed main(void)
 23 {
 24     scanf("%d%d%d", &n, &m, &k);
 25     
 26     for (int i = 1; i <= m; ++i)
 27         scanf("%d%d%d",
 28             &e[i].x,
 29             &e[i].y,
 30             &e[i].c);
 31     
 32     bool possible = true;
 33     
 34     {
 35         int cnt = 0, root;
 36         
 37         for (int i = 1; i <= n; ++i)fa[i] = i;
 38         
 39         for (int i = 1; i <= m; ++i)
 40             if (e[i].c)
 41             {
 42                 int fx = find(e[i].x);
 43                 int fy = find(e[i].y);
 44                 
 45                 if (fx != fy)
 46                     fa[fx] = fy;
 47             }
 48         
 49         for (int i = 1; i <= m; ++i)
 50             if (!e[i].c)
 51             {
 52                 int fx = find(e[i].x);
 53                 int fy = find(e[i].y);
 54                 
 55                 if (fx != fy)
 56                     fa[fx] = fy, ++cnt, vis[i] = true;
 57             }
 58         
 59         if (cnt > k)
 60             possible = false;
 61             
 62         root = find(1);
 63         
 64         for (int i = 2; i <= n; ++i)
 65             if (find(i) != root)
 66                 possible = false;
 67         
 68         k -= cnt;
 69     }
 70     
 71     if (!possible)
 72         return puts("no solution"), 0;
 73     
 74     {
 75         for (int i = 1; i <= n; ++i)fa[i] = i;
 76         
 77         for (int i = 1; i <= m; ++i)
 78             if (vis[i])
 79             {
 80                 int fx = find(e[i].x);
 81                 int fy = find(e[i].y);
 82                 
 83                 fa[fx] = fy;
 84             }
 85         
 86         for (int i = 1; i <= m; ++i)
 87             if (!e[i].c && !vis[i] && k)
 88             {
 89                 int fx = find(e[i].x);
 90                 int fy = find(e[i].y);
 91                 
 92                 if (fx != fy)
 93                     fa[fx] = fy, vis[i] = true, --k;
 94             }
 95         
 96         for (int i = 1; i <= m; ++i)
 97             if (e[i].c)
 98             {
 99                 int fx = find(e[i].x);
100                 int fy = find(e[i].y);
101                 
102                 if (fx != fy)
103                     fa[fx] = fy, vis[i] = true;
104             }
105     }
106     
107     for (int i = 1; i <= m; ++i)
108         if (vis[i])printf("%d %d %d\n", e[i].x, e[i].y, e[i].c);
109 }

 

@Author: YouSiki

 

posted @ 2017-02-22 20:02  YouSiki  阅读(228)  评论(0编辑  收藏  举报