「PKUWC2018」猎人杀(概率+容斥+分治NTT)

https://loj.ac/problem/2541

很有意思的一道题目。

直接去算这题话,因为分母会变,你会发现不管怎么样都要枚举顺序。

考虑把题目转换,变成分母不会变的,即对于一个已经删过的,我们不把它从分母中剔除,但是,每一次的选择需要一直选直到选了一个没有被删过的。

然后再考虑怎么计算,这时就可以容斥了:
1既然要最后删除,我们枚举一个集合S一定在它之后被删,其它的随意。
\(sw\)\(\sum_{i\in S}w[i]\)\(W=\sum_{i=1}^n w[i]\)
最后答案就是\(\sum_{S}(-1)^{|S|}\sum_{i=0}^{∞}({W-sw-w[1] \over W})^i*{w[1] \over W}\)
\(=\sum_S (-1)^{|S|}{W\over sw+w[1]}*{w[1]\over W}\)
\(=\sum_S (-1)^{|S|} {w[1] \over sw+w[1]}\)

用一个分治NTT优化一下背包就好了。

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;
}

#define V vector<ll>
#define si size()
#define re resize

namespace ntt {
	const int nm = 131072;
	int r[nm]; ll w[nm], a[nm], b[nm];
	void build() {
		for(int i = 1; i < nm; i *= 2) {
			w[i] = 1; ll v = ksm(3, (mo - 1) / 2 / i);
			ff(j, 1, i) w[i + j] = w[i + j - 1] * v % mo;
		}
	}
	void dft(ll *a, int n, int f) {
		ff(i, 0, n) {
			r[i] = r[i / 2] / 2 + (i & 1) * (n / 2);
			if(i < r[i]) swap(a[i], a[r[i]]);
		} ll b;
		for(int i = 1; i < n; i *= 2) for(int j = 0; j < n; j += 2 * i) ff(k, 0, i)
			b = a[i + j + k] * w[i + k], a[i + j + k] = (a[j + k] - b) % mo, a[j + k] = (a[j + k] + b) % mo;
		if(f == -1) {
			reverse(a + 1, a + n);
			b = ksm(n, mo - 2);
			ff(i, 0, n) a[i] = (a[i] + mo) * b % mo;
		}
	}
	V operator * (V p, V q) {
		int n0 = p.si + q.si - 1, n = 1;
		while(n < n0) n *= 2;
		ff(i, 0, n) a[i] = b[i] = 0;
		ff(i, 0, p.si) a[i] = p[i];
		ff(i, 0, q.si) b[i] = q[i];
		dft(a, n, 1); dft(b, n, 1);
		ff(i, 0, n) a[i] = a[i] * b[i] % mo;
		dft(a, n, -1);
		p.re(n0);
		ff(i, 0, p.si) p[i] = a[i];
		return p;
	}
}

using ntt :: operator *;

const int N = 1e5 + 5;

int n, w[N];

V dg(int x, int y) {
	if(x > y) {
		V p; p.re(1); p[0] = 1;
		return p;
	}
	if(x == y) {
		V p; p.clear(); p.re(w[x] + 1);
		p[0] = 1; p[w[x]] = -1;
		return p;
	}
	int m = x + y >> 1;
	return dg(x, m) * dg(m + 1, y);
}

int main() {
	ntt :: build();
	scanf("%d", &n);
	fo(i, 1, n) scanf("%d", &w[i]);
	V p = dg(2, n);
	ll ans = 0;
	ff(i, 0, p.si) ans = (ans + p[i] * ksm(i + w[1], mo - 2) % mo * w[1]) % mo;
	ans = (ans % mo + mo) % mo;
	pp("%lld\n", ans);
}
posted @ 2020-04-02 15:27  Cold_Chair  阅读(184)  评论(0编辑  收藏  举报