【JZOJ6376】【NOIP2019模拟2019.10.05】樱符[完全墨染的樱花]

题目大意

给出一个无向图,当边权全部为\(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\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/11625087.html
今日推荐