题面好评
题意简述:\(n\)个点的无向仙人掌,求\(\sum_{u=1}^n\sum_{v=1}^n f(u, v)\cdot p^{(u-1)\cdot n+v}\),其中\(f(u,u)=0\),\(f(u,v)=\)以\(u\)为源点,\(v\)为汇点的最大流。
先考虑一棵树的情况。将边按流量从大到小排序之后依次加入,显然最大流被第\(i\)条边限制的点对均在\(u_i\)和\(v_i\)所在的联通块之间。对每个联通块维护\(\sum p^i\)和\(\sum p^{(i-1)\cdot n}\)即可。
结论:仙人掌上删掉每个环上流量最小的边,并将它的流量加到环上剩余的边后,任意两点间的最大流不变。
证明:显然仙人掌上每个环对最大流的影响是独立的。考虑\(f(u, v)\)经过的每一个环,删边之前存在两条路径,其中一条经过最小边,另一条为删去最小边后的简单路径,显然删边操作不会对环上的限制产生影响。
因此最大生成树,链覆盖即可,仙人掌上覆盖的总边数是\(O(n)\)级别,不需要用数据结构维护。
#include <cstdio>
#include <cctype>
#include <cassert>
#include <cstring>
#include <iostream>
#include <algorithm>
#define R register
#define ll long long
using namespace std;
const int N = 310000, M = N << 1, mod = 998244353;
int n, m, p, fat[N], hd[N], to[M], nxt[M], noedg = 1, fa[N], cap[M], dep[N], num, val[N];
ll pw[N], pwn[N], sum[N], sumn[N], ans;
struct EDGE {
int x, y, vis, cap;
EDGE (int x = 0, int y = 0, int v = 0, int c = 0) : x(x), y(y), vis(v), cap(c) {}
inline bool operator < (const EDGE &a) const {
return cap < a.cap;
}
}edg[M];
template <class T> inline void read(T &x) {
x = 0;
char ch = getchar(), w = 0;
while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x = w ? -x : x;
return;
}
int find(int x) {
return fat[x] == x ? x : (fat[x] = find(fat[x]));
}
inline ll addMod(ll a, ll b) {
return (a += b) >= mod ? a - mod : a;
}
void unite(int x, int y) {
fat[x] = y;
sum[y] = addMod(sum[y], sum[x]);
sumn[y] = addMod(sumn[y], sumn[x]);
return;
}
void dfs(int now) {
dep[now] = dep[fa[now]] + 1;
for (R int i = hd[now], v; i; i = nxt[i]) {
if ((v = to[i]) == fa[now]) continue;
fa[v] = now, val[v] = cap[i], dfs(v);
}
return;
}
inline void addEdg(int x, int y, int c) {
nxt[++noedg] = hd[x], hd[x] = noedg, to[noedg] = y, cap[noedg] = c;
nxt[++noedg] = hd[y], hd[y] = noedg, to[noedg] = x, cap[noedg] = c;
return;
}
inline void modify(int x, int y, int w) {
if (dep[x] < dep[y]) swap(x, y);
while (dep[x] != dep[y]) val[x] += w, x = fa[x];
while (x != y)
val[x] += w, x = fa[x], val[y] += w, y = fa[y];
return;
}
int main() {
read(n), read(m), read(p);
pw[0] = pwn[0] = 1;
for (R int i = 1; i <= n; ++i)
pw[i] = pw[i - 1] * p % mod;
for (R int i = 1; i <= n; ++i)
pwn[i] = pwn[i - 1] * pw[n] % mod;
for (R int i = 1; i <= n; ++i)
fat[i] = i;
for (R int i = 1; i <= m; ++i)
read(edg[i].x), read(edg[i].y), read(edg[i].cap);
sort(edg + 1, edg + 1 + m);
for (R int i = m; i; --i) {
int x = find(edg[i].x), y = find(edg[i].y);
if (x != y)
unite(x, y), addEdg(edg[i].x, edg[i].y, edg[i].cap), edg[i].vis = 1;
}
dfs(1);
for (R int i = 1; i <= m; ++i) {
if (edg[i].vis) continue;
modify(edg[i].x, edg[i].y, edg[i].cap);
}
for (R int i = 1; i <= n; ++i)
sum[i] = pw[i], sumn[i] = pwn[i - 1], fat[i] = i;
for (R int i = 2; i <= n; ++i)
edg[++num] = EDGE(i, fa[i], 0, val[i]);
sort(edg + 1, edg + n);
for (R int i = n - 1; i; --i) {
int x = find(edg[i].x), y = find(edg[i].y);
if (x != y) {
ans = (ans + (sumn[x] * sum[y] + sumn[y] * sum[x]) % mod * edg[i].cap) % mod;
unite(x, y);
}
}
cout << ans << endl;
return 0;
}