题意
给你一个图,问有多少个最小生成树,要求输出最小生成树个数乘以最小生成树的权值。
思路
数据是那样生成的,有相同的权值的边的概率特别小,可以视作不会发生(测试用例中也的确没有这样的数据)。题目中的代码 + 套Kruscal算法的代码就能过。
实际在做,考虑了重边的问题,u,v两个点如果有多条直连边,在Kruscal算法要取u,v的连线时,取其中最小的那个,并算出u,v之间一共有多少个权值与最小的那个相同的,记为num1。同理其他的两个点,记为num2、num3……
最后算出最小生成树的权值ans后,ans再乘以
(
n
u
m
1
∗
n
u
m
2
∗
…
…
∗
n
u
m
x
)
(num1*num2*……*num_x)
(num1∗num2∗……∗numx),记得取模。
但是这样做,如果题目给出的数据中,含有环,并且环上的最大值有多个,那么还是能hack掉这份代码的。
不过数据出的不好,一下子就过了。
实际要确保无误的做,还是得按题目提到的方法来做。
如下链接:https://blog.csdn.net/qq_40679299/article/details/88196872
代码
#include <bits/stdc++.h>
using namespace std;
const unsigned long long mod = 1e9 + 7;
const int maxn = 1e5 + 5;
unsigned long long k1, k2;
unsigned long long xorShift128Plus()
{
unsigned long long k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
int n, m;
struct edge{
int u, v;
unsigned long long w;
}a[maxn];
bool cmp(edge e1, edge e2)
{
if(e1.w == e2.w){
if(e1.u == e2.u)
{
return e1.v < e2.v;
}
return e1.u < e2.u;
}
return e1.w < e2.w;
}
int fa[maxn];
void init_union_find(int n)
{
for(int i = 1; i <= n; i++)
fa[i] = i;
return ;
}
int find_fa(int x)
{
if(x == fa[x]){
return x;
}else{
return fa[x] = find_fa(fa[x]);
}
}
void unite(int u, int v)
{
u = find_fa(u);
v = find_fa(v);
fa[v] = u;
return;
}
bool same(int u, int v)
{
u = find_fa(u);
v = find_fa(v);
if(u == v)
return true;
return false;
}
unsigned long long Kruskal()
{
sort(a + 1, a + m + 1, cmp);
init_union_find(n);
unsigned long long res = 0;
unsigned long long ans = 1;
int cntcnt = 0;
for(int i = 1; i <= m; i++)
{
edge e = a[i];
if(!same(e.u, e.v))
{
unite(e.u, e.v);
res = (res + e.w) % mod;
cnt++;
}
}
if(cntcnt != n - 1)
return 0;
return (res * ans) % mod;
}
void gen()
{
scanf("%d%d%llu%llu", &n, &m, &k1, &k2);
for(int i = 1; i <= m; ++i)
{
a[i].u = xorShift128Plus() % n + 1;
a[i].v = xorShift128Plus() % n + 1;
a[i].w = xorShift128Plus();
if(a[i].u > a[i].v)
swap(a[i].u, a[i].v);
}
}
int main(){
int t;
scanf("%d", &t);
while(t--)
{
gen();
cout << Kruskal() << endl;
}
return 0;
}