题解 [ABC328E] Modulo MST
cjh20090318 · · 题解
不是很难的 ABC E,甚至相比于以前的简单。
题意
给你一个
分析
观察到数据范围其实很小,所以先从朴素算法开始分析,考虑直接枚举子集。
但是每个子集组成的边不一定是一棵树,一棵树显然要满足任意两个点之间有且只有一条边连接。
所以枚举边集有且仅能有
所以时间复杂度是 但是因为本人懒得写搜索直接用的枚举二进制位。
代码
//the code is from chenjh
#include<cstdio>
#include<algorithm>
typedef long long LL;
int n,m;
LL k;
struct Edge{
int u,v;
LL w;
}e[30];
int f[10];
int fd(const int x){return x==f[x]?x:f[x]=fd(f[x]);}//并查集路径压缩找根。
int main(){
scanf("%d%d%lld",&n,&m,&k);
for(int i=0;i<m;i++) scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].w);
LL s=k;
for(unsigned int x=0,mx=1u<<m;x<mx;x++)if(__builtin_popcount(x)==n-1){//判断是否刚好选了 n-1 条边。
f[1]=1,f[2]=2,f[3]=3,f[4]=4,f[5]=5,f[6]=6,f[7]=7,f[8]=8;//对并查集进行初始化,循环展开加快速度。
bool sol=1;LL w=0;
int g=n;//连通块数量。
for(int i=0;i<m;i++)if((x>>i)&1){
if(fd(e[i].u)==fd(e[i].v)){sol=0;break;}//每两点之间有且仅有一条路径,如果已经连接说明不合法。
f[fd(e[i].u)]=fd(e[i].v),--g;//成功合并一次减少一个连通块。
w=(w+e[i].w)%k;
}
if(sol && g==1) s=s<w?s:w;//合法就取最小值。
}
printf("%lld\n",s);
return 0;
}