hdu 4786 Fibonacci Tree【生成树相关】

探讨了在给定的无向图中寻找包含特定数量白色边(斐波那契数)的生成树的可能性。利用并查集算法确定图的连通性,并计算所需白色边的数量范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Fibonacci Tree

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4514    Accepted Submission(s): 1412

Problem 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 <= 105) and M(0 <= M <= 105).
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

Source

2013 Asia Chengdu Regional Contest

Recommend

We have carefully selected several similar problems for you:  5921 5920 5919 5918 5917  


题目大意:


一共有N个点,M条无向边,其中每条边要么是白色的要么是黑色的(白色的用边权值为1表示),然后让你判断能否从图中找出一棵生成树,其中有n-1条边,其中白色的边的个数为斐波那契数,如果可行输出Yes,否则输出No。


思路:


1、吐槽一发这个题意..............看着区域赛的榜单顺序做的这个题,翻译完之后觉得是不能使用黑边的,题意确实很难真正透彻读懂...................


2、我们首先需要判断这样的图是否是连通的。如果当前图是不连通的,那么直接输出No,否则我们这样考虑:

①如果我们枚举一个斐波那契数num,表示当前生成树中的白色边的条数,那么我们很难确定如何找这num条白边,使得这个图尽量能够有一颗生成树。

②那么我们可以考虑极限情况,我们只要知道最少需要多少条白边,最多需要多少条白边,那么我们如果有一个斐波那契数<=maxn&&>=minn即可。

③minn求法:我们将所有黑色边都加入图中,这里用并查集连通即可,然后统计构成一颗生成树需要多少条白色边,这个统计的数,就是最少需要的边的个数。

④maxn求法:我们将所有白色边都加入图中,同样可以用并查集连通,然后统计最多需要多少条这样的白色边即可,统计出来的这个数,就是最多需要的边的个数。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node
{
    int x,y,w;
}a[100050];
int f[100050];
int fi[10050];
void init()
{
    fi[1]=1;
    fi[2]=2;
    int brek;
    for(int i=3;i<=100;i++)
    {
        fi[i]=fi[i-1]+fi[i-2];
        if(fi[i]>1000000)
        {
            brek=i;
            break;
        }
    }
}
int find(int a)
{
    int r=a;
    while(f[r]!=r)
    {
        r=f[r];
    }
    int i=a;
    int j;
    while(i!=r)
    {
        j=f[i];
        f[i]=r;
        i=j;
    }
    return r;
}
void merge(int x,int y)
{
    int xx=find(x);
    int yy=find(y);
    if(xx!=yy)
    {
        f[xx]=yy;
    }
}
int cmp(node a,node b)
{
    return a.w<b.w;
}
int cmp2(node a,node b)
{
    return a.w>b.w;
}
int main()
{
    int t;
    int kase=0;
    init();
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)f[i]=i;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
            merge(a[i].x,a[i].y);
        }
        int flag=0;
        for(int i=2;i<=n;i++)
        {
            if(find(i)!=find(1))
            {
                flag=1;
            }
        }
        printf("Case #%d: ",++kase);
        if(flag==1)
        {
            printf("No\n");
            continue;
        }
        for(int i=1;i<=n;i++)f[i]=i;
        sort(a,a+m,cmp);
        int minn=0;
        for(int i=0;i<m;i++)
        {
            if(a[i].w==0)
            {
                if(find(a[i].x)!=find(a[i].y))
                {
                    merge(a[i].x,a[i].y);
                }
            }
            else
            {
                if(find(a[i].x)!=find(a[i].y))
                {
                    merge(a[i].x,a[i].y);
                    minn++;
                }
            }
        }
        for(int i=1;i<=n;i++)f[i]=i;
        sort(a,a+m,cmp2);
        int maxn=0;
        for(int i=0;i<m;i++)
        {
            if(a[i].w==0)
            {
                if(find(a[i].x)!=find(a[i].y))
                {
                    merge(a[i].x,a[i].y);
                }
            }
            else
            {
                if(find(a[i].x)!=find(a[i].y))
                {
                    merge(a[i].x,a[i].y);
                    maxn++;
                }
            }
        }
        flag=0;
        for(int i=1;i<=25;i++)
        {
            if(fi[i]>=minn&&fi[i]<=maxn)
            {
                printf("Yes\n"),flag=1;
                break;
            }
        }
        if(flag==0)
        printf("No\n");
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值