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条边,边有两种,一种黑边权值为0,一种白边权值为1,在图中构成一颗生成树,使这棵树的总权值为一个fibonacci数。有就输出Yes,没有就输出No。
因为是找一棵生成树的权值大小,所以肯定使用kruskal或者prim算法,但是这里点有1e5这么多个,所以只能使用kruskal。
然后就是理思路,首先这个图必须是联通图,不然就不能构成一颗生成树,这种情况输出No。然后要找一个生成树的权值为一个fibonacci数字,只需要求出能构成生成树的权值的最大值max和最小值min,只要这之间存在这么一个数字就行了。所以接下来就是求一次最小生成树的最大值和最小值,这个范围之间如果有就输出Yes,没有就输出No。
然后我就按这个思路敲了,敲完一些低级错误,学长指导下也改掉了,然后ac 了。然后就还有一个疑问,就是怎么保证最小生成树max和min之间的每一个权值都成立。然后百度一下,大概懂了,就是先最大边排序跑kruskal,权值为max,这时就是用最多白边构成的生成树,换一种思考的方法,这时用一条黑边来代替白边,然后生成树的权值就为max-1,然后重复这个过程,当没有黑边能代替白边时,然后此时构成的树就是最小生成树能构成的最小值min,就是这么个道理。反过来用黑边构成的最小生成树推max也是一样的.
据我了解,那个max应该是最大生成树。
CODE
#include"stdio.h"
#include"iostream"
#include"algorithm"
#include"string.h"
#include"math.h"
#include"stdlib.h"
#include"queue"
#define maxn 100000+10
#define inf 77777777
using namespace std;
struct node ///边
{
int st;
int en;
int len;
}edge[maxn];
int n,m; ///n个点,m条边
int fa[maxn]; ///用来判断是否都属于一个父亲
int fib[35]; ///肺部辣鸡数列
bool cmp1(node a,node b) ///递增排序
{
if(a.len < b.len)
return true;
return false;
}
bool cmp2(node a,node b) ///递减排序
{
if(a.len > b.len)
return true;
return false;
}
int Find(int x) ///寻找根节点
{
if(fa[x] == x) return x;
else return fa[x] = Find(fa[x]);
}
int kruskal() ///模版
{
for(int i = 0;i <= n;i++) ///每个点的父亲都是本身
{
fa[i] = i;
}
int ans = 0;
for(int i = 1;i <= m;i++)
{
int fx = Find(edge[i].st);
int fy = Find(edge[i].en);
if(fx != fy) ///能否构成环,不是同一个根节点就把边权值加起来并改变父亲
{
ans += edge[i].len;
fa[fx] = fy;
}
}
return ans;
}
int main(void)
{
fib[0] = fib[1] = 1;
for(int i = 2;i <= 31;i++) ///肺部辣鸡
fib[i] = fib[i-2]+fib[i-1];
int T;
scanf("%d",&T);
for(int j = 1;j <= T;j++)
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++)
{
scanf("%d%d%d",&edge[i].st,&edge[i].en,&edge[i].len);
}
printf("Case #%d: ",j);
sort(edge+1,edge+m+1,cmp1); ///边进行递增排序
int ans1 = kruskal(); ///黑边优先排序构成最小生成树min
int flag = 0; ///标记是否联通
int temp = Find(fa[1]); ///记录一个点父亲
for(int i = 1;i <= n;i++) ///如果有一个点的根节点不是temp就flag=1
if(Find(fa[i]) != temp)
{
flag = 1;break;
}
if(flag == 1) ///不连通就输出No
{
printf("No\n");
continue;
}
sort(edge+1,edge+m+1,cmp2); ///边递减排序
int ans2 = kruskal(); ///白边优先的最大生成树max
int lead = 0;
for(int i = 1;i <= 30;i++) ///判断区间是否有肺部辣鸡数
{
if(fib[i] >= ans1 && fib[i] <= ans2)
{
lead = 1;
break;
}
}
if(lead == 0) ///输出
{
printf("No\n");
}
else
{
printf("Yes\n");
}
}
return 0;
}