Fibonacci Tree HDU - 4786
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
题意:
给你一个图有1边和0边两种边,问你能否构成一个生成树使得所有边权加和为斐波那契数
分析:
做法是先生成一个最小生成树,即1边最少的情况,得到权值mins,再生成一个最大生成树,即1边最多的情况,得到权值maxs,那么这之间的所有数都对应一个权值为这个数的生成树
为什么,我tm也没想明白为什么,网上很多博客只说了做法,我只能结合一下,说一下笼统的想法
证明及其不严谨,仅供理解
利用数学归纳法证明如果最小为mins,最大为maxs,那么一定可以通过mins+1,mins+1+1…推到maxs
当点的个数为2的时候
最大是0,最小是1,一定成立
假设当节点个数为n的时候,该结论成立
我们证明有n+1个点时成立
假设现在我们的生成树是权值最小时的数,即权值为mins
假设我们要换一条0边,将这条0边删去之后一定变成了两棵树设为T1,T2
1.如果v1,v2中间恰好有一条1边,这样结论成立,得到mins+1
2.如果v1,v2中间没有1边可以替换,但是我们知道存在一个最大值maxs,那么说明一定存在一个联通区域,可以替换0边是1边增加,我们就假设是T1,因为T1的节点数小于等于n,且上面假设了n时结论成立,即当前T1的权值一定是小于最大权值的,也就是一定能得到具有当前权值+1的权值的新树,所以整个树也就得到了权值+1的新树,由此结论成立
code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
struct Node{
int u,v,w;
}edge[maxn];
bool cmp1(Node a,Node b){
return a.w > b.w;
}
bool cmp2(Node a,Node b){
return a.w < b.w;
}
int pre[maxn],fib[maxn],n,m;
int init(){
fib[1] = 1;
fib[2] = 2;
for(int i = 3; fib[i] < maxn; i++){
fib[i] = fib[i-1] + fib[i-2];
if(fib[i] >= maxn)
return i;
}
}
int Find(int x){
if(x == pre[x])
return x;
else return pre[x] = Find(pre[x]);
}
int Kruskal(){
for(int i = 0; i <= n; i++){
pre[i] = i;
}
int num = 0,sum = 0;
for(int i = 0; i < m; i++){
int u = Find(edge[i].u);
int v = Find(edge[i].v);
if(u != v){
pre[u] = v;
num++;
sum += edge[i].w;
}
if(num == n - 1) break;
}
if(num == n-1) return sum;
else return 0;
}
int main(){
int T,cas = 0;
int cnt = init();
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edge[i].u = u;
edge[i].v = v;
edge[i].w = w;
}
sort(edge,edge+m,cmp1);
int maxs = Kruskal();
sort(edge,edge+m,cmp2);
int mins = Kruskal();
bool flag = 0;
for(int i = mins; i <= maxs; i++){
if(flag) break;
for(int k = 1; k < cnt; k++){
if(i == fib[k]){
flag = 1;
break;
}
}
}
if(flag) printf("Case #%d: Yes\n",++cas);
else printf("Case #%d: No\n",++cas);
}
return 0;
}