题目大意
给出一个无向图,当边权全部为(1)时满足任意两点间最大流(leq 2),现在给每条边赋权,一对点((s,t))的贡献等于(maxflow(s,t)*p^{(s-1)n+t}),其中(p)是一个给定的数,求所有点对贡献之和。
Solution
题目描述里隐含着这个无向图是仙人掌。证明很简单,若两个简单环有交,其中必有一对点的最大流为(3),与题设矛盾,因此简单环都没有交,这个图是个仙人掌。
当然看出这个条件没什么用,最重要的是要把最大流转换成最小割处理。仙人掌上两点间的最小割只有两种可能,一种是割掉树上的边,一种是割掉环上两条边。可以发现环上的最小边是必定会割掉的,于是我们可以预先把这条边割掉,然后把环上其它边都加上这条边的权值。这时仙人掌变成了一棵树,可以发现两点间最小割就是路径上边的最小值,做一遍最大生成树即可。至于(p^{(s-1)n+t}),用并查集维护即可。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 300007, M = 1000007;
const ll P = 998244353;
inline int read() {
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
int n, m;
ll ans, p, s1[N], s2[N];
ll pow(ll a, ll b) {
ll ret = 1;
for (; b; a = a * a % P, b >>= 1) if (b & 1) ret = ret * a % P;
return ret;
}
int cnt, total;
struct edge {
int from, to, len;
} e[M], arr[N];
int cmp(edge a, edge b) { return a.len > b.len; }
ll inc[N];
int tot, st[N], to[M], nx[M], len[M], fa[N], anc[N][20], size[N], vis[M], dep[N];
void add(int u, int v, int w) {
to[++tot] = v, nx[tot] = st[u], len[tot] = w, st[u] = tot;
}
int getfa(int x) { return fa[x] == x ? x : getfa(fa[x]); }
void link(int x, int y) {
x = getfa(x), y = getfa(y);
if (size[x] > size[y]) swap(x, y);
fa[x] = y, size[y] += size[x], s1[y] = (s1[y] + s1[x]) % P, s2[y] = (s2[y] + s2[x]) % P;
}
void dfs(int u) {
for (int j = 1; j <= 19; ++j) anc[u][j] = anc[anc[u][j - 1]][j - 1];
for (int i = st[u]; i; i = nx[i]) if (to[i] != anc[u][0]) dep[to[i]] = dep[u] + 1, anc[to[i]][0] = u, dfs(to[i]);
}
int getlca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = 19; i >= 0; --i) if (dep[anc[u][i]] >= dep[v]) u = anc[u][i];
if (u == v) return u;
for (int i = 19; i >= 0; --i) if (anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i];
return anc[u][0];
}
void doit(int u) {
for (int i = st[u]; i; i = nx[i]) if (to[i] != anc[u][0]) doit(to[i]), arr[++cnt] = (edge){u, to[i], len[i] + inc[to[i]]}, inc[u] += inc[to[i]];
}
int main() {
freopen("sakura.in", "r", stdin);
freopen("sakura.out", "w", stdout);
n = read(), m = read(), p = read();
for (int i = 1; i <= m; ++i) e[i].from = read(), e[i].to = read(), e[i].len = read();
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= n; ++i) fa[i] = i, size[i] = 1;
for (int i = 1; i <= m; ++i) {
int u = getfa(e[i].from), v = getfa(e[i].to);
if (u == v) vis[i] = 1;
else link(u, v), add(e[i].from, e[i].to, e[i].len), add(e[i].to, e[i].from, e[i].len);
}
dep[1] = 1, dfs(1);
for (int i = 1; i <= m; ++i) if (vis[i]) {
int lca = getlca(e[i].from, e[i].to);
inc[e[i].from] += e[i].len, inc[e[i].to] += e[i].len, inc[lca] -= 2 * e[i].len;
}
doit(1);
sort(arr + 1, arr + cnt + 1, cmp);
for (int i = 1; i <= n; ++i) fa[i] = i, s1[i] = pow(p, i), s2[i] = pow(p, 1ll * (i - 1) * n), size[i] = 1;
for (int i = 1; i <= cnt; ++i) {
int u = getfa(arr[i].from), v = getfa(arr[i].to);
ans = (ans + 1ll * s1[u] * s2[v] % P * arr[i].len % P) % P;
ans = (ans + 1ll * s2[u] * s1[v] % P * arr[i].len % P) % P;
link(u, v);
}
printf("%lld
", ans);
return 0;
}