刚读题还以为是WJMZBMR出的tree(bzoj2654)...
其实只用分别把黑边放在前面 白边放在前面做mst,统计两次用的白边数量,看看这个区间内是否有fbi数就可以了
代码写的很挫而且由于数组开小RE了好几次-_-#
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int MAX=100010;
vector<int> WS,wt,bs,bt;
int fbi[MAX],maxf,uni[MAX],us[MAX],ut[MAX],uc[MAX],N,M,f1;
void pre()
{
memset(fbi,0,sizeof(fbi));
fbi[1]=1;
fbi[2]=2;
maxf=2;
while (fbi[maxf]<=100000)
{
maxf++;
fbi[maxf]=fbi[maxf-1]+fbi[maxf-2];
}
}
int find(int x)
{
int t=x,r;
while(x!=uni[x]) x=uni[x];
while(t!=uni[t])
{
r=uni[t];
uni[t]=x;
t=r;
}
return x;
}
void union1(int x,int y)
{
int a=find(x),b=find(y);
uni[a]=uni[b];
}
int mst()
{
memset(uni,0,sizeof(uni));
for (int i=1; i<=N; i++)
uni[i]=i;
int ans=0,num=0;
for (int i=1; i<=M; i++)
{
if (num==N-1) return ans;
if (find(us[i])!=find(ut[i]))
{
union1(us[i],ut[i]);
if (uc[i]==1) ans++;
num++;
}
}
if (num!=N-1) f1=false;
return ans;
}
int main()
{
freopen("in.in","r",stdin);
pre();
int T;
scanf("%d",&T);
for (int Ti=1; Ti<=T; Ti++)
{
scanf("%d%d",&N,&M);
int s,t,c;
for (int i=1; i<=M; i++)
{
scanf("%d%d%d",&s,&t,&c);
if (c==1)
{
WS.push_back(s);
wt.push_back(t);
}
else
{
bs.push_back(s);
bt.push_back(t);
}
}
for (int i=0; i<bs.size(); i++)
{
us[i+1]=bs[i];
ut[i+1]=bt[i];
uc[i+1]=0;
}
f1=true;
for (int i=0; i<WS.size(); i++)
{
us[i+bs.size()+1]=WS[i];
ut[i+bt.size()+1]=wt[i];
uc[i+bt.size()+1]=1;
}
int l=mst();
for (int i=0; i<WS.size(); i++)
{
us[i+1]=WS[i];
ut[i+1]=wt[i];
uc[i+1]=1;
}
for (int i=0; i<bs.size(); i++)
{
us[i+WS.size()+1]=bs[i];
ut[i+wt.size()+1]=bt[i];
uc[i+wt.size()+1]=0;
}
int r=mst();
bool flag=false;
for (int i=1; i<=maxf; i++)
if (fbi[i]>=l&&fbi[i]<=r)
{
flag=true;
break;
}
printf("Case #%d: ",Ti);
if (flag&&f1) printf("Yes\n");
else printf("No\n");
WS.clear();
wt.clear();
bs.clear();
bt.clear();
}
return 0;
}