[BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

题目链接:BZOJ - 1016

题目分析

最小生成树的两个性质:

同一个图的最小生成树,满足:

1)同一种权值的边的个数相等

2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

最后根据乘法原理将各种边的选法数乘起来就可以了。

特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm> using namespace std; const int MaxN = 100 + 5, MaxM = 1000 + 5, Mod = 31011; int n, m, Top, Sum, Cnt;
int f[MaxN], L[MaxM], R[MaxM], Num[MaxM]; typedef long long LL;
LL Ans; struct Edge
{
int u, v, w;
} E[MaxM]; inline bool Cmp(Edge e1, Edge e2)
{
return e1.w < e2.w;
} inline int Find(int x, int o)
{
int i, j, k;
j = x;
while (j != f[j]) j = f[j];
if (o == 1) return j;
i = x;
while (i != j)
{
k = i;
i = f[i];
f[k] = j;
}
return j;
} void DFS(int Type, int x, int y)
{
if (x == R[Type] + 1)
{
if (y == Num[Type]) ++Cnt;
return;
}
int fx, fy;
fx = Find(E[x].u, 1); fy = Find(E[x].v, 1);
if (fx != fy)
{
f[fx] = fy;
DFS(Type, x + 1, y + 1);
f[fx] = fx;
}
DFS(Type, x + 1, y);
} int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i)
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
sort(E + 1, E + m + 1, Cmp);
Top = 0; Sum = 0;
int fx, fy;
for (int i = 1; i <= n; ++i) f[i] = i;
for (int i = 1; i <= m; ++i)
{
if (i == 1 || E[i].w != E[i - 1].w) L[++Top] = i;
R[Top] = i;
fx = Find(E[i].u, 0); fy = Find(E[i].v, 0);
if (fx != fy)
{
f[fx] = fy;
++Num[Top];
++Sum;
}
}
if (Sum != n - 1)
{
printf("0\n");
return 0;
}
for (int i = 1; i <= n; ++i) f[i] = i;
Ans = 1;
for (int i = 1; i <= Top; ++i)
{
if (Num[i] == 0) continue;
Cnt = 0;
DFS(i, L[i], 0);
Ans = Ans * (LL)Cnt % Mod;
for (int j = L[i]; j <= R[i]; ++j)
{
fx = Find(E[j].u, 0); fy = Find(E[j].v, 0);
if (fx != fy) f[fx] = fy;
}
}
printf("%d\n", (int)Ans);
return 0;
}

  

上一篇:Python 官方文件


下一篇:selenium之批量执行测试用例