题面好评
题意简述:\(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; }