先把边按权值排序。假设现在考虑到的是所有权值为
w
的边,并且所有权值小于
归纳一下,得出结论:在一张图的每一个最小生成树中,同一权值的边的出现次数和连通的点集相同。
因此,先将边按权值排序,然后做一遍Kruskal,求出每种权值边的出现次数。
然后按顺序考虑每种权值:由于同种权值的边数
≤10
,因此可以
210
暴力枚举每条边选或不选,只要没有环并且选出边的条数恰好等于预处理出的次数,这种选法就是合法的。这时候,这种权值的结果就是合法方案的个数。
最后把每种权值的合法方案个数相乘,得到最终结果。
实现上的细节:
1、考虑完一种权值之后,要保留这种权值的边连通的集合。具体用个例子:
n=4
,
m=5
,有边
(1,2,1)
,
(1,3,1)
,
(2,3,2)
,
(2,4,2)
,
(3,4,2)
。(三元组的第三个元素表示边权)这时候如果不保留连通的集合,那么权值
1
的结果为
2、注意图不联通的情况,输出
0
<script type="math/tex" id="MathJax-Element-6403">0</script>。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 105, M = 1005, ZZQ = 31011;
int n, fa[N], m, d, cnt[M], pyz[M], sel[M], ans[M], L[M], R[M], F[M], lyt[M];
struct cyx {
int u, v, w;
} orz[M];
bool comp(cyx a, cyx b) {
return a.w < b.w;
}
int cx(int x) {
if (fa[x] != x) fa[x] = cx(fa[x]);
return fa[x];
}
bool zm(int x, int y) {
int ix = cx(x), iy = cx(y);
if (ix == iy) return 0;
return fa[iy] = ix, 1;
}
void kruskal() {
int i; for (i = 1; i <= n; i++) fa[i] = i;
for (i = 1; i <= m; i++) if (zm(orz[i].u, orz[i].v)) sel[i] = 1;
}
void dfs(int cyx, int dep, int sta) {
if (dep == R[cyx] + 1) {
int i, cnt = 0; for (i = 1; i <= n; i++) fa[i] = F[i];
for (i = 0; i < R[cyx] - L[cyx] + 1; i++)
if ((sta >> i) & 1) cnt++; if (cnt != pyz[cyx]) return;
for (i = 0; i < R[cyx] - L[cyx] + 1; i++)
if (((sta >> i) & 1) && !zm(orz[L[cyx] + i].u, orz[L[cyx] + i].v))
return; ans[cyx]++;
if (ans[cyx] == 1) for (i = 1; i <= n; i++) lyt[i] = fa[i];
return;
}
dfs(cyx, dep + 1, sta);
dfs(cyx, dep + 1, sta | (1 << dep - L[cyx]));
}
int main() {
int i, j; n = read(); m = read();
for (i = 1; i <= m; i++) orz[i].u = read(), orz[i].v = read(),
orz[i].w = read(); sort(orz + 1, orz + m + 1, comp);
kruskal(); for (i = 1; i <= m;) {
L[++d] = i; for (j = i; orz[i].w == orz[j].w && j <= m; j++)
cnt[d]++, pyz[d] += sel[j]; R[d] = j - 1;
i = j;
}
for (i = 1; i <= n; i++) fa[i] = i;
for (i = 1; i <= d; i++) if (pyz[i] && cnt[i] == 1)
zm(orz[L[i]].u, orz[L[i]].v);
for (i = 1; i <= n; i++) F[i] = fa[i], fa[i] = i;
for (i = 1; i <= d; i++) {
if (!pyz[i]) continue;
if (cnt[i] == 1) ans[i] = 1;
else {
dfs(i, L[i], 0);
for (j = 1; j <= n; j++) F[j] = lyt[j];
}
}
int wohaocaia = 1; for (i = 1; i <= d; i++) if (pyz[i])
wohaocaia = wohaocaia * ans[i] % ZZQ;
for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++)
zm(orz[i].u, orz[i].v); for (i = 2; i <= n; i++)
if (cx(1) != cx(i)) return printf("0\n"), 0;
cout << wohaocaia << endl;
return 0;
}