解法
是Google Kickstart公开课里的解法:
首先,需要分析出,当每个点都找它们直接相邻的边里的最小值时,距离最近。每个点都只取与它直接相连且距离最小的那条边时,我们可以得到一张子图:
[外链图片转存失败(img-AzLi7slT-1562315720963)(quiver-image-url/31F02DCC828C6FB099EE1387BFD57340.jpg =90x94)]
可以证明这个子图里没有环:假如存在
A->B->C->A
的环,那么可以知道|AB|>|BC|>|CA|>|AB|
,这就矛盾了
所以,这个子图是个没有环的无向图,因此一定是个森林。
对于森林里的每棵树,通过BFS分层之后,相邻两层的点着色不同就行了,单数层有2种着色法
剩下一种特殊情况:边长度为0
长度为0的边着色一定是不同的,与这条边相邻的点其实可以随意着色:
[外链图片转存失败(img-Pf2OdYLo-1562315720968)(quiver-image-url/8768E25DA6CFB5AEC95AB2A70AA32567.jpg =374x139)]
所以最后的算法是:
- 通过选择最近边,把子图抽出来
- 计算出子图有
N
个连通分量 - 计算出子图有
M
个与长度为0的边相连的点(但是它们的边长度不是0) - 最后的答案就是
2^(M+N)
#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <cmath>
#include <unordered_map>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
#include <functional>
#define MAXN 60
#define INF 100010
using namespace std;
typedef long long lld;
int v,e;
int f[MAXN];
pair<int,int> nearest[MAXN];
int find(int x) {
int r = x;
while(f[r]!=r) r = f[r];
while(f[x]!=r) {
int tmp = f[x];
f[x] = r;
x = tmp;
}
return r;
}
bool join(int x,int y) {
int rx = find(x), ry = find(y);
if(rx!=ry) {
f[rx] = ry;
return true;
}
return false;
}
lld solve() {
for(int i=1;i<=v;++i) {
f[i] = i;
nearest[i].first = 0;
nearest[i].second = INF;
}
pair<int,int> zero;
zero.first = zero.second = -1;
for(int i=0;i<e;++i) {
int a,b,l;
scanf("%d%d%d",&a,&b,&l);
if(l<nearest[a].second) {
nearest[a].first = b;
nearest[a].second = l;
}
if(l<nearest[b].second) {
nearest[b].first = a;
nearest[b].second = l;
}
if(l==0) {
zero.first = a;
zero.second = b;
}
}
int n=v;
for(int i=1;i<=v;++i) {
if(join(i,nearest[i].first)) n--;
}
// printf("%d\n",n);
if(zero.first!=-1) {
for(int i=1;i<=v;++i) {
if(i!=zero.first && i!=zero.second && (nearest[i].first==zero.first || nearest[i].first==zero.second)) {
n++;
// printf("node = %d, to %d, length = %d\n",i,nearest[i].first,nearest[i].second);
}
}
}
// printf("%d %d\n",m,n);
return 1L<<n;
}
int main() {
// freopen("/Users/huangyuemei/CLionProjects/untitled/B-small-practice.in","r",stdin);
// freopen("/Users/huangyuemei/CLionProjects/untitled/my.out","w",stdout);
int t;
scanf("%d",&t);
for(auto round=1;round<=t;++round) {
scanf("%d%d",&v,&e);
printf("Case #%d: %lld\n",round,solve());
}
}