[SOJ635] 完全墨染的樱花

谁都会走 提交于 2019-11-30 21:01:34

题面好评

题意简述:\(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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!