zoukankan      html  css  js  c++  java
  • 【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
    ", ans);
    	return 0;
    }
    
    
  • 相关阅读:
    新手入门:python的pip安装(二)
    新手入门:python的安装(一)
    python解释器和环境安装
    Python基础入门总结
    Python环境搭建教程(windows10)
    漏洞重温之sql注入(三)
    漏洞重温之sql注入(二)
    漏洞重温之sql注入(一)
    漏洞重温之文件上传(FUZZ)
    漏洞重温之文件上传(总结)
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11625087.html
Copyright © 2011-2022 走看看