hdu 4786 Fibonacci Tree 思路

Description

  Coach Pang is interested in Fibonacci numbers while Uncle Yang wants him to do some research on Spanning Tree. So Coach Pang decides to solve the following problem: 
  Consider a bidirectional graph G with N vertices and M edges. All edges are painted into either white or black. Can we find a Spanning Tree with some positive Fibonacci number of white edges? 
(Fibonacci number is defined as 1, 2, 3, 5, 8, ... )

Input

  The first line of the input contains an integer T, the number of test cases. 
  For each test case, the first line contains two integers N(1 <= N <= 10  5) and M(0 <= M <= 10  5). 
  Then M lines follow, each contains three integers u, v (1 <= u,v <= N, u<> v) and c (0 <= c <= 1), indicating an edge between u and v with a color c (1 for white and 0 for black).

Output

  For each test case, output a line “Case #x: s”. x is the case number and s is either “Yes” or “No” (without quotes) representing the answer to the problem.

Sample Input

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

Sample Output

Case #1: Yes

Case #2: No

题意:

给你n个点,m条边,边有黑白两种颜色,问能否用这些边组成一个生成树,使其白色的边的个数为斐波拉切数(1,2,3,5)?

解题思路:

先用 kruskal跑一遍看图是否连通。然后只用白色边构图(用kruskal跑),看最多能用多少白色边(max),然后只用黑色边构图,看最少能用多少白色边(min)。最后枚举斐波拉切数,看是否有在这个范围内成立的斐波拉切数。

为什么找到了max图和min图就能保证一定存在这之间任意数量的白色边的图?

考察min图,加入一条未被用过的白色边,有两种情况

1:这个加入的边刚好覆盖在一条黑色边上,删除此黑色边即可得到min+1的图;

2:这个边没有覆盖任何边,那么只要能从联通这个边的两个节点的通路上删除一条黑色边,就可以min+1的图。假设不存在min+1的图,那么此通路上的所有边都是白色,那么直到把所有白色边都加入图中,任意白色边的两个端点间的通路就都没有黑色边。那么就会得到这样一种情况,最多只有一个节点既能连白色边,又能连黑色边,删除此节点,则得到两个不相连图,一个全是黑色边,一个全是白色边。对于这种情况,可以推出max=min,也就是说只要max>min,这个假设就是错的,所以min+1的图一定存在。

继续考察min+1的图,同上证明。。。可以证明白色边数量在min-max之间所有的图都存在。

好烦人的题目。。。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
using namespace std;
int f[maxn],febo[50];
int n,m;
struct edge
{
    int u,v,c;
}e[maxn];
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int solve(int col)
{
    int num=0;
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(e[i].c!=col)
        {
            int x=find(e[i].u),y=find(e[i].v);
            if(x!=y)
            {
                f[x]=y;
                num++;
            }
        }
    }
    return num;
}
int main()
{
    febo[0]=1,febo[1]=2;
    int num;
    for(num=2;;num++)
    {
        febo[num]=febo[num-1]+febo[num-2];
        if(febo[num]>100000)
        break;
    }
    int ncase,T=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        printf("Case #%d: ",++T);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
        int tmp,mi,ma,tru=0;
        tmp=solve(2);
        if(tmp!=n-1)
        {
            printf("No\n");
            continue;
        }
        ma=solve(0);
        mi=n-1-solve(1);
        for(int i=0;i<num;i++)
        {
            if(febo[i]>=mi&&febo[i]<=ma)
            {
                tru=1;
                break;
            }
        }
        if(tru)
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值