HDU4786_Fibonacci Tree
题目很新颖的,略带智商,很好。
题目的意思是给你一些白色边和黑色边,现在问你能否用两色边构造出一颗生成树,且树中白色边的数量为一个Fibonacci数。
其实在没做题目之前我就已经听说了这个题目的解题方法了。所以。。。。。
是这样做的,我们首先判断把所有的边都加进去,看看这个图是不是连通的,如果不是,那么显然我们可以直接输出NO了。
接下来是一个很有趣的方法。我们首先把所有两端点在不同集合的白边加入到同一集合,看看最多能加多少条,再按照同样的方法把黑边加入同一集合,看看能加入多少条。
这样一来我们等于是把树中能够包含的白色边的最大数和最小数都求出来了,接下来我们只要看看在这个区间中间有木有一个Fibonacci数就可以了。
也许有人会有疑惑为什么求出最大边数和最小边数中间的每一个边数状态都可以达到呢?
其实可以这样来理解,对于一棵树由于白色边和黑色边两种,我们在任意一个状态,我们可以取出一条黑边然后用一条白边代替这条黑边并且保证这个树是联通的。
因为我们已经把最大的边数和最小的边数都算出来了。
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 100100
using namespace std;
struct edge{
int u,v;
}a[2][maxn];
int s[2];
int n,m,c,t,uu,vv,cc,MM,mm,cas=0;
int f[maxn],g[maxn];
bool flag;
void init() { for (int i=1; i<=n; i++) f[i]=i; }
void build(int xx,int yy) { f[f[xx]]=f[yy]; }
int getf(int x)
{
if (f[x]!=x) f[x]=getf(f[x]);
return f[x];
}
void addedge(int x)
{
int tot=0;
init();
for (int i=1; i<=s[x]; i++)
if (getf(a[x][i].u)!=getf(a[x][i].v)) tot++,build(a[x][i].u,a[x][i].v);
if (x==0) mm=n-1-tot;
else MM=tot;
}
int main()
{
g[1]=1,g[2]=2;
for (int i=2; g[i]<=maxn; i++) g[i+1]=g[i]+g[i-1];
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
s[0]=s[1]=0;
for (int i=1; i<=m; i++)
{
scanf("%d%d%d",&uu,&vv,&cc);
a[cc][++s[cc]].u=uu;
a[cc][s[cc]].v=vv;
}
init();
for (int i=1; i<=s[0]; i++)
if (getf(a[0][i].u)!=getf(a[0][i].v)) build(a[0][i].u,a[0][i].v);
for (int i=1; i<=s[1]; i++)
if (getf(a[1][i].u)!=getf(a[1][i].v)) build(a[1][i].u,a[1][i].v);
flag=true;
for (int i=2; i<=n; i++)
if (getf(i)!=getf(i-1)) { flag=false; break; }
if (!flag)
{
printf("Case #%d: No\n",++cas);
continue;
}
addedge(0),addedge(1);
//cout<<" MM && mm : " <<mm<<' '<<MM<<endl;
flag=false;
for (int i=1; g[i]<=n; i++)
if (g[i]<=MM && g[i]>=mm) { flag=true; break; }
if (flag) printf("Case #%d: Yes\n",++cas);
else printf("Case #%d: No\n",++cas);
}
return 0;
}
如有转载,请注明出处(http://www.cnblogs.com/lochan)