3624: [Apio2008]免费道路
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
/* 比较优秀的一道题
我们对于这个K上来第一反应可能就是先找k个0,别的再说
但这个显然错误。(没准只有我个蒟蒻真试了=-=)
但是我们反过来想,如果c值为1的边和小于k条的c值为0的
边,能够使得这个图成立(即树)。那么我们将其中的一些c值
为1的边替换成c值为0的边,这个图还能成立。
证明我们可以这么想,首先我们已经搞出了一棵树,当我们
砍掉一条树枝时,就分成了两个树(这里一个点也看作一棵树),
即树a,树b,我们刚才砍了一条边,就要补一条边,补上的这
条边显然不可以是a中一点连a中另一点,b同理(万恶的并查集)
,那么只能是a中的某一点连到b中的某一点,那么此时它又变成
了一棵树。
同时还有一件事就是,对于c值为1的边,无论链接顺序怎样
最后连接的边数都是定值。(这里指用c值为1的边贪心的生成树)。
然后一切就都好办了,先贪心跑一遍c值为1的边,再在此基
础上跑c值为0的边(即最少需要c值为0的边的边数)。若此时不
能构成一颗完全的树,或者最少需要的c值为0的边的边数都大于
K显然无解。反之,我们把刚才用到的c值为0的边全部使用,再
补上剩下c值为0的边来满足K,如果不够k,无解。否则我们再跑
c值为1的就好了。
【注】 这个大视野卡回车啊!!!
1 #include<cstdio> 2 using namespace std; 3 const int N=100100; 4 struct edges{int u,v,c;}edge[N]; 5 int n,m,k; 6 int stack[N],top; 7 int beg[2],ed[2]; 8 int fa[N]; 9 bool chos[N]; 10 int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]);} 11 int sum[2]; 12 void solve(int op,int up){ 13 for(int i=1;i<=m&&sum[op]<up;i++){ 14 if(edge[i].c==op){ 15 int u=edge[i].u,v=edge[i].v; 16 u=find(u),v=find(v); 17 if(u!=v){ 18 sum[op]++;fa[v]=u;stack[++top]=i;chos[i]=true; 19 } 20 } 21 } 22 } 23 24 void special(){ 25 for(int i=1;i<=m;i++){ 26 if(edge[i].c==0&&chos[i]){ 27 int u=edge[i].u,v=edge[i].v; 28 u=find(u),v=find(v); 29 if(u!=v){ 30 fa[v]=u; 31 stack[++top]=i; 32 sum[0]++; 33 } 34 } 35 } 36 } 37 int main(){ 38 scanf("%d%d%d",&n,&m,&k); 39 for(int i=1;i<=m;i++){ 40 scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c); 41 } 42 for(int i=1;i<=n;i++) fa[i]=i; 43 solve(1,N); 44 solve(0,N); 45 if(sum[0]+sum[1]<n-1||sum[0]>k){ 46 printf("no solution\n"); 47 return 0; 48 } 49 top=0; 50 for(int i=1;i<=n;i++){fa[i]=i;} 51 sum[0]=sum[1]=0; 52 special(); 53 solve(0,k); 54 if(sum[0]<k) { 55 printf("no solution\n");return 0; 56 } 57 solve(1,n-1-k); 58 for(int i=1,j;i<=top;i++){ 59 j=stack[i]; 60 printf("%d %d %d\n",edge[j].u,edge[j].v,edge[j].c); 61 } 62 }
没有什么不可能。