UOJ #181. 【UR #12】密码锁(竞赛图+概率)

http://uoj.ac/problem/181

题解:

这个需要知道竞赛图统计强联通分量的个数的思路(套路)。

考虑竞赛图缩图之后是一条链,枚举这条链的一条边,把图分为左右两部分,之间的边是单向的就数量+1。

这题n比较大,不能暴力\(O(2^n)\)

但是可以对每个特殊边的联通块做一遍,那么其它边都是\(1/2\)可以快速算。

最后是若干OGF卷起来,暴力即可。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 998244353;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const ll ni2 = ksm(2, mo - 2);

const int N = 40;

int n, m;
ll x, y, z;

ll aw[N * N], bw[N * N], cw[N * N];

ll a[N][N];

vector<int> e[N];
#define pb push_back
#define si size()

int bz[N], d[N], d0;

void dg(int x) {
	if(bz[x]) return;
	bz[x] = 1;
	d[++ d0] = x;
	ff(_y, 0, e[x].si) {
		int y = e[x][_y];
		dg(y);
	}
}

ll f[N], g[N], h[N];

int p[N], p0, q[N], q0;

void juanji() {
	fo(i, 0, n) h[i] = f[i], f[i] = 0;
	fo(i, 0, n) if(h[i]) fo(j, 0, n - i) if(g[j]) {
		f[i + j] = (f[i + j] + h[i] * g[j] % mo) % mo;
	}
}

ll sum = 0;

void dfs(int x) {
	if(x > d0) {
		ll s = 1;
		fo(i, 1, p0) fo(j, 1, q0)
			s = s * a[p[i]][q[j]] % mo;
		s = s * bw[p0 * q0] % mo;
		if(p0 > 0 && p0 < n) sum = (sum + s * aw[p0 * (n - p0)] % mo) % mo;
		g[p0] = (g[p0] + s) % mo;
		return;
	}
	p[++ p0] = d[x];
	dfs(x + 1);
	p0 --;
	q[++ q0] = d[x];
	dfs(x + 1);
	q0 --;
}

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, n) {
		fo(j, i + 1, n) a[i][j] = a[j][i] = 5000;
	}
	aw[0] = 1; fo(i, 1, n * n) aw[i] = aw[i - 1] * 5000 % mo;
	fo(i, 0, n * n) bw[i] = ksm(aw[i], mo - 2);
	cw[0] = 1; fo(i, 1, n * n) cw[i] = cw[i - 1] * 10000 % mo;
	fo(i, 1, m) {
		scanf("%lld %lld %lld", &x, &y, &z);
		a[x][y] = z; a[y][x] = 10000 - z;
		e[x].pb(y); e[y].pb(x);
	}
	f[0] = 1;
	fo(i, 1, n) if(!bz[i]) {
		d0 = 0;
		dg(i);
		fo(j, 0, n) g[j] = 0;
		dfs(1);
		juanji();
	}
	ll ans = 0;
	fo(i, 1, n - 1) ans = (ans + f[i] * aw[i * (n - i)] % mo * cw[n * (n - 1) / 2 - i * (n - i)]) % mo;
	ans = (ans + cw[n * (n - 1) / 2]) % mo;
	ans = (ans % mo + mo) % mo;
	ans = ans * cw[n * (n - 1) / 2] % mo;
	pp("%lld\n", ans);
}
posted @ 2020-05-26 16:58  Cold_Chair  阅读(361)  评论(0编辑  收藏  举报