题目传送门


题意:给你一个图,图中边颜色为白色(1表示)或者黑色(0表示),问你是否能找出一棵树,使这棵树中白边的总数量的值为Fib数。点数n和变数m均为不大于1e5的数。

分析:这是最小生成树的变形题,用Kruskal求解,我们可以通过求两次最小生成树得到白边数量的上下限,再判断这个区间内有没有fib数即可,第一次我们以黑边为主排序求最小生成树,得到白边数量下限,再以白边为主排序求最小生成树得到白边数量上限。注意如果一开始图不连通,那么输出No。


ac代码如下

#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<iostream>
#include<algorithm>
using namespace std;

bool fib[100010];
int fa[100010];
struct node{
   int st,en,len;
}E[100010];
int n,m;

int Find(int x)
{
    if(x==fa[x])
        return x;
    else
        return fa[x]=Find(fa[x]);
}

void init()
{
    memset(fib,false,sizeof(fib));
    int a=1,b=2,c;
    fib[a]=fib[b]=true;
    while(c<=100000)
    {
        c=a+b;
        fib[c]=true;
        a=b;
        b=c;
    }
}

bool cmp1(const node &a,const node &b)
{
    return a.len<b.len;
}
bool cmp2(const node &a,const node &b)
{
    return a.len>b.len;
}

int main()
{
    int tt,ncase=1;
    scanf("%d",&tt);
    init();
    while(tt--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&E[i].st,&E[i].en,&E[i].len);
        }
        int ans1=0,ans2=0;
        sort(E+1,E+m+1,cmp1);
        for(int i=1;i<=n;i++)
        {
            fa[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            int fx=Find(E[i].st);
            int fy=Find(E[i].en);
            if(fx!=fy)
            {
                ans1+=E[i].len;
                fa[fx]=fy;
            }
        }
        bool ok=true;
        int tmp=Find(1);
        for(int i=2;i<=n;i++)
        {
            if(Find(i)!=tmp)ok=false;
        }
        if(!ok)
        {
            printf("Case #%d: No\n",ncase++);
            continue;
        }
        sort(E+1,E+m+1,cmp2);
        for(int i=1;i<=n;i++)
        {
            fa[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            int fx=Find(E[i].st);
            int fy=Find(E[i].en);
            if(fx!=fy)
            {
                ans2+=E[i].len;
                fa[fx]=fy;
            }
        }
        bool flag=false;
        for(int i=ans1;i<=ans2;i++)
        {
            if(fib[i])
            {
                flag=true;
                break;
            }
        }
        if(flag)
            printf("Case #%d: Yes\n",ncase++);
        else
            printf("Case #%d: No\n",ncase++);
    }
    return 0;
}