E - Modulo MST
Problem Statement
You are given a weighted simple connected undirected graph with
N
N
N vertices and
M
M
M edges, where vertices are numbered
1
1
1 to
N
N
N, and edges are numbered
1
1
1 to
M
M
M. Additionally, a positive integer
K
K
K is given.
Edge
i
(
1
≤
i
≤
M
)
i\ (1\leq i\leq M)
i (1≤i≤M) connects vertices
u
i
u_i
ui and
v
i
v_i
vi and has a weight of
w
i
w_i
wi.
For a spanning tree T T T of this graph, the cost of T T T is defined as the sum, modulo K K K, of the weights of the edges in T T T. Find the minimum cost of a spanning tree of this graph.
Constraints
- 2 ≤ N ≤ 8 2\leq N\leq8 2≤N≤8
- N − 1 ≤ M ≤ N ( N − 1 ) 2 N-1\leq M\leq\dfrac{N(N-1)}2 N−1≤M≤2N(N−1)
- 1 ≤ K ≤ 1 0 15 1\leq K\leq10^{15} 1≤K≤1015
- 1 ≤ u i < v i ≤ N ( 1 ≤ i ≤ M ) 1\leq u_i\lt v_i\leq N\ (1\leq i\leq M) 1≤ui<vi≤N (1≤i≤M)
- 0 ≤ w i < K ( 1 ≤ i ≤ M ) 0\leq w_i\lt K\ (1\leq i\leq M) 0≤wi<K (1≤i≤M)
- The given graph is simple and connected.
- All input values are integers.
Input
The input is given from Standard Input in the following format:
N N N M M M K K K
u 1 u_1 u1 v 1 v_1 v1 w 1 w_1 w1
u 2 u_2 u2 v 2 v_2 v2 w 2 w_2 w2
⋮ \vdots ⋮
u M u_M uM v M v_M vM w M w_M wM
Output
Print the answer.
题目大意
给定一个无向图,要求输出生成树的权值模 k k k的最小值。
思路
这道题乍一看是一道最小生成树的板子,但是题目规定生成树的权值为原本权值模k,这样要求的话,我们再用贪心来写这道题就不可行了。
我们注意到,题目给定的
N
N
N的范围特别小,计算一下会发现,8个点的生成树只有262144种。这样我们可以考虑枚举所有生成树的方法,计算其结果模k的最小值即可。
具体做法为:我们用dfs枚举生成树的所有状态,每次枚举完八个点之后用并查集判断一下是否存在重复路径,如果不重复,则更新结果,最终结果即为答案。
实现
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
#define int long long
#define ULL unsigned long long
#define PII pair<int, int>
#define lowbit(x) (x & -x)
#define Mid ((l + r) >> 1)
#define ALL(x) x.begin(), x.end()
#define endl '\n'
#define fi first
#define se second
const int INF = 0x7fffffff;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, m, k, res;
int fa[N], a[10][100], pre[10];
int find(int x) {//查询根节点
return (x == fa[x]) ? x : fa[x] = find(fa[x]);
}
void dfs(int cur) {
if(cur > n) {
int sum = 0;
for(int i = 1; i <= n; i ++ ) {
fa[i] = i;
}
for(int i = 2; i <= n; i ++ ) {
if(find(pre[i]) == find(i)) return; //已经存在路径的情况下就不构成生成树了
fa[find(i)] = find(pre[i]); //合并
sum += a[i][pre[i]];
}
res = min(res, sum % k);
return;
}
for(int i = 1; i <= n; i ++ ) {
if(a[cur][i] == -1) continue; //不存在边
pre[cur] = i; //记录其子节点
dfs(cur + 1);
}
}
void Solved() {
cin >> n >> m >> k;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= n; j ++ )
a[i][j] = -1;
for(int i = 0; i < m; i ++ ) {
int x, y, w; cin >> x >> y >> w;
a[x][y] = a[y][x] = w;
}
res = k;
dfs(2);
cout << res << endl;
}
signed main(void) {
IOS
int ALL = 1;
//cin >> ALL;
while(ALL -- ) Solved();
return 0;
}