组队赛第四场:最小生成树变形

组队赛第四场:

HDU 4786 Fibonacci Tree 

思路:

这题比赛的时候想了好久,最后队友机智的想到了。

不过那时不是我敲的,现在敲的1A。

想好就容易了。

直接把1或者0当做边的权值,然后按边从小到大排序,然后算最小生成用到了几条白边,然后再按边从大到小排序,然后再算白边用了几条。然后最小和最大需要用到的白边都算出来了。如果在这最小最大区间中存在那个啥数列的话就是Yes,否则就是No。

为什么在这区间里面就是对的呢?刚开始我也想了好久,然后发现,因为白边权值是1,然后黑边是0,然后假设用到白边最小的是6,最大的是10,那么,我们可以用黑边去替代两条白边,生成树就是有8条白边了,而正好是我们要找的数列中的数。其实还是有点抽象……自己脑子想想吧……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define INF 510010
#define maxn 1000010
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int f[maxn],fib[92];
struct abc
{
    int u,v,w;
}a[maxn];
bool cmp1(abc a,abc b)
{
    return a.w<b.w;
}
bool cmp2(abc a,abc b)
{
    return a.w>b.w;
}
void Fib()
{
    fib[1]=1;fib[2]=2;
    for(int i=3;i<41;i++)
        fib[i]=fib[i-1]+fib[i-2];
}
int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
void init()
{
    for(int i=0;i<maxn;i++)
        f[i]=i;
}
int kruscal(int n,int m)
{
    init();
    int i,sum=0,num=0,flag=1;
    for(i=0;i<n&&flag;i++)
    {
        int x=find(a[i].u);
        int y=find(a[i].v);
        if(x!=y)
        {
            f[y]=x;
            sum+=a[i].w;
            num++;
        }
        if(num==m-1) flag=0;
    }
    if(!flag) return sum;
    else return -1;
}
int main()
{
    //freopen("test.txt","r",stdin);
    int t;
    cin>>t;
    Fib();
    for(int kk=1;kk<=t;kk++)
    {
        int n,m,i;
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++)
            scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
        sort(a,a+m,cmp1);
        int sum1=kruscal(m,n);
        sort(a,a+m,cmp2);
        int sum2=kruscal(m,n);
        int cnt1=lower_bound(fib+1,fib+41,sum1)-fib;
        int cnt2=lower_bound(fib+1,fib+41,sum2)-fib;
        if(sum1==-1) printf("Case #%d: No\n",kk);//不存在最小生成树
        else if(cnt1!=cnt2||cnt1==cnt2&&sum1==sum2&&fib[cnt1]==sum1)
            printf("Case #%d: Yes\n",kk);
        else printf("Case #%d: No\n",kk);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值