多校省选模拟8

多校省选模拟8

体育测试(test)

题意

给你 \(N\) 个人,你要给他们排队,每个人有一个限制,表示自己的位置必须 \(\ge a_i\),或者是 \(\le a_i\)

问有多少种合法的排队方法。

\(N\le 5000\),答案对于 \(998244353\) 取模。

题解

我们考虑,如果都只有一类限制的话,答案会成啥,比如都是 \(\le a_i\) 的限制。

那么我们只要按照 \(a_i\) 从小到大排序,然后我们的答案自然就是 \(\prod_{i=1}^N(a_i-i+1)\)

我们考虑,把一个 \(\ge a_i\) 的限制,容斥成为 \(\le n\) 的限制和 \(< a_i\) 的限制即可。

那么,我们令 \(f(i,j)\) 表示做到第 \(i\) 个,然后一共现在满足了 \(j\) 个限制的方案数。

那么,对于一个本来就是 \(\le a_i\) 的限制,她一定要被满足,转移就是

\[f(i,j)=f(i-1,j-1)\times (a_i-j+1) \]

这部分是不需要容斥的。

我们考虑对于原来的一个 \(\ge a_i\) 的限制,她分为两种转移,一个是用了 \(\le n\) 的限制,一个是用了 \(< a_i\) 的限制。分别就是

\[f(i,j)=f(i-1,j)+(-1)(a_i-j)f(i-1,j-1) \]

然后,我们考虑,最后的答案自然就是

\[\sum_{i=tot}^Nf(N,i)\times (N-i)! \]

乘上的 \((N-i)!\) 的系数表示那些选择了 \(\le N\) 的限制的那些人随便排列。

#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::pair;
template<typename T>
inline T max(const T &x, const T &y) {
	return x > y ? x : y;
}
const int MAXN = 5e3 + 10, MOD = 1e9 + 7;
int N, fac[MAXN], F[MAXN][MAXN];
pair<int, int> A[MAXN];
int main() {
	freopen("test.in", "r", stdin);
	freopen("test.out", "w", stdout);
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	fac[0] = 1;
	for (int i = 1; i < MAXN; ++i) {
		fac[i] = (long long) fac[i - 1] * i % MOD;
	}
	cin >> N;
	for (int i = 1; i <= N; ++i) {
		cin >> A[i].second;
		if (A[i].second > 0) {
			A[i].first = A[i].second;
		}
		else {
			A[i].first = -A[i].second - 1;
		}
	}
	std::sort(A + 1, A + 1 + N);
	F[0][0] = 1;
	int tot = 0;
	for (int i = 1; i <= N; ++i) {
		if (A[i].second > 0) {
			++tot;
			for (int j = 1; j <= i && A[i].second - j >= 0; ++j) {
				F[i][j] = F[i - 1][j - 1] * (A[i].second - j + 1LL) % MOD;
			}
		}
		else {
			for (int j = 0; j <= i; ++j) {
				F[i][j] = F[i - 1][j];
				if (j && A[i].first - j >= 0) {
					F[i][j] = (F[i][j] + (MOD - 1LL) * (A[i].first - j + 1) % MOD * F[i - 1][j - 1]) % MOD;
				}
			}
		}
	}
	int ANS = 0;
	for (int i = tot; i <= N; ++i) {
		ANS = (ANS + (long long) F[N][i] * fac[N - i]) % MOD;
	}
	cout << ANS << '\n';
	return 0;
}

一个奇怪的问题就是,在使用 std::sort 的时候,我原本的代码并没有把这个 \(A[i]\) 表示为 pair<int, int> 类型,而是定义成了 int,然后写了个排序函数

sort(A + 1, A + 1 + N, [&] (const int &x, const int &y) -> {
	if (x * y > 0) {
        return abs(x) < abs(y);
    }
    else if (x < 0) {
        return -x - 1 <= y;
    }
    else {
        return x <= -y - 1;
    }
});

然后就 runtime error 了。看来以后要注意这种奇怪的 STL 问题。

贸易(trade)

题意

给你一个 \(N\) 个点的树,然后,每次你可以在树上走,每次你要卖一个货物,,由 \(A\) 的价格购入,\(B\) 的价格售出,然后你可以选择一个起点,走到终点把货物卖出去,但是卖成功的概率只有 $ ( 1-\frac{x_i}{y_i})^{len}$ 的概率,其中 \(len\) 是路径的长度,问每次给你 \(A,B,x_i,y_i\),然后你随机一个路径,问期望收益。

题解

考场上看出来了,然后不会多点求值。我们考虑把所有情况的收益都算出来,然后除以 \(N^2\) 即可。

我们考虑长度为 \(k\) 的路径有 \(cnt_k\) 条,然后我们的答案就是 \(\frac{B\sum_{i=0}^{N-1}cnt_i p^i}{N^2}-A\)

首先 \(cnt_i\) 就是一个点分治然后就能求出来,那么我们的要点就是求 \(\sum_{i=0}^{N-1}cnt_i p^i\) 的值。

这明显就是一个以 \(cnt\) 为系数的多项式在 \(p_i\) 处的点值。

我们只要多点求值就完了。

#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::swap;
using std::cerr;
using std::function;
using std::pair;
template <typename T>
inline T min(const T &x, const T &y) {
	return x < y ? x : y;
}
template<typename T>
inline T max(const T &x, const T &y) {
	return x > y ? x : y;
}
const int MAXN = 262144, MOD = 998244353;
auto Ksm = [] (int x, int y) -> int {
	int ret = 1;
	for (; y; y /= 2, x = (long long) x * x % MOD) {
		if (y & 1) {
			ret = (long long) ret * x % MOD;
		} 
	}
	return ret;
};
auto Mod = [] (int x) -> int {
	if (x >= MOD) {
		return x - MOD;
	}
	else if (x < 0) {
		return x + MOD;
	}
	else {
		return x;
	}
};
inline int ls(int k) {
	return k << 1;
}
inline int rs(int k) {
	return k << 1 | 1;
}
int SZ, R[MAXN], W[MAXN];
void INIT(int len) {
	if (SZ == len) {
		return;
	}
	SZ = len;
	for (int i = 1; i < len; ++i) {
		R[i] = (R[i >> 1] >> 1) | (i & 1 ? (len >> 1) : 0);
	}
	int wn = Ksm(3, (MOD - 1) / len);
	W[len >> 1] = 1;
	for (int i = (len >> 1) + 1; i < len; ++i) {
		W[i] = (long long) W[i - 1] * wn % MOD;
	}
	for (int i = (len >> 1) - 1; i > 0; --i) {
		W[i] = W[i << 1];
	}
	return;
}
void Ntt(vector<int>& F, int limit, int type) {
	static unsigned long long c[MAXN];
	copy(F.begin(), F.begin() + limit, c);
	for (int i = 1; i < limit; ++i) {
		if (i < R[i]) {
			swap(c[i], c[R[i]]);
		}
	}
	for (int o = 2, j = 1; o <= limit; o <<= 1, j <<= 1) {
		for (int i = 0; i < limit; i += o) {
			for (int k = 0; k < j; ++k) {
				unsigned long long OI = c[i + j + k] * W[k + j] % MOD;
				c[i + j + k] = c[i + k] + MOD - OI;
				c[i + k] += OI;
			}
		}
	}
	if (type == -1) {
		reverse(c + 1, c + limit);
		int inv = Ksm(limit, MOD - 2);
		for (int i = 0; i < limit; ++i) {
			c[i] = c[i] * inv % MOD;
		}
	}
	for (int i = 0; i < limit; ++i) {
		F[i] = c[i] % MOD;
	}
	return;
}
struct Poly {
	vector<int> v;
	int& operator [] (const int &pos) {
		return v[pos];
	}
	int len() {
		return v.size();
	}
	void set(int l) {
		return v.resize(l);
	}
	void clear() {
		v.clear();
		return;
	}
	void adjust() {
		while (v.size() > 1 && !v.back()) {
			v.pop_back();
		}
		return;
	}
	void rev() {
		reverse(v.begin(), v.end());
	}
	void Ntt(int L, int type) {
		int limit = 1 << L;
		INIT(limit);
		set(limit);
		::Ntt(v, limit, type);
		return;
	}
	void Squ() {
		int L = ceil(log2(len())) + 1, limit = 1 << L;
		Ntt(L, 1);
		for (int i = 0; i < limit; ++i) {
			v[i] = (long long) v[i] * v[i] % MOD;
		}
		Ntt(L, -1);
		return adjust();
	}
	void operator += (Poly &x) {
		if (len() < x.len()) {
			set(x.len());
		}
		for (int i = 0; i < x.len(); ++i) {
			v[i] = Mod(v[i] + x[i]);
		}
		adjust();
		return;
	}
	void operator -= (Poly &x) {
		if (len() < x.len()) {
			set(x.len());
		}
		for (int i = 0; i < x.len(); ++i) {
			v[i] = Mod(v[i] - x[i]);
		}
		adjust();
		return;
	}
	Poly operator * (Poly &x) {
		Poly ret, tmp0 = *this, tmp1 = x;
		int L = ceil(log2(tmp0.len() + tmp1.len() - 1)), n = 1 << L;
		Ntt(L, 1);
		x.Ntt(L, 1);
		ret.set(n);
		for (int i = 0; i < n; ++i) {
			ret[i] = (long long) x[i] * v[i] % MOD;
		}
		ret.Ntt(L, -1);
		ret.adjust();
		*this = tmp0;
		x = tmp1;
		return ret;
	}
	Poly operator - (Poly &x) {
		Poly ret;
		ret.set(max(len(), x.len()));
		for (int i = 0; i < len(); ++i) {
			ret[i] = v[i];
		}
		for (int i = 0; i < x.len(); ++i) {
			ret[i] = Mod(ret[i] - x[i]);
		}
		return ret;
	}
	Poly operator + (Poly &x) {
		Poly ret;
		ret.set(max(len(), x.len()));
		for (int i = 0; i < len(); ++i) {
			ret[i] = v[i];
		}
		for (int i = 0; i < x.len(); ++i) {
			ret[i] = Mod(ret[i] + x[i]);
		}
		return ret;
	}
	void operator *= (Poly &x) {
		Poly tmp = x;
		int L = ceil(log2(len() + x.len() - 1)), n = 1 << L;
		Ntt(L, 1);
		x.Ntt(L, 1);
		for (int i = 0; i < n; ++i) {
			v[i] = (long long) v[i] * x[i] % MOD;
		}
		Ntt(L, -1);
		adjust();
		x = tmp;
	}
	Poly GetInv(int deg = -1) {
		if (!~deg) {
			deg = len();
		}
		if (deg == 1) {
			return {{Ksm(v[0], MOD - 2)}};
		}
		Poly ret = GetInv((deg + 1) / 2), tmp;
		int L = ceil(log2(deg)) + 1, n = 1 << L, mx = min(len(), deg);
		tmp.set(deg);
		for (int i = 0; i < mx; ++i) {
			tmp[i] = v[i];
		}
		tmp.Ntt(L, 1);
		ret.Ntt(L, 1);
		for (int i = 0; i < n; ++i) {
			ret[i] = (2 - (long long) tmp[i] * ret[i] % MOD + MOD) * ret[i] % MOD;
		}
		ret.Ntt(L, -1);
		ret.set(deg);
		return ret;
	}
	pair<Poly, Poly> operator % (Poly &x) {
		if (x.len() > len()) {
			return {{{0}}, *this};
		}
		Poly tmp0 = *this, tmp1 = x;
		tmp0.rev();
		tmp1.rev();
		tmp1 = tmp1.GetInv(len() - x.len() + 1);
		tmp0 *= tmp1;
		tmp0.set(len() - x.len() + 1);
		tmp0.rev();
		tmp1 = tmp0 * x;
		Poly ret = *this - tmp1;
		ret.set(x.len() - 1);
		return {tmp0, ret};
	}
	vector<int> getmulpointvalue(vector<int> x) {
		static Poly tmp[MAXN * 4];
		function<void(int, int, int)> get = [&] (int u, int l, int r) -> void {
			if (l == r) {
				tmp[u] = {{Mod(-x[l]), 1}};
				return;
			}
			int mid = (l + r) / 2;
			get(ls(u), l, mid);
			get(rs(u), mid + 1, r);
			tmp[u] = tmp[ls(u)] * tmp[rs(u)];
			return;
		};
		get(1, 0, x.size() - 1);
		vector<int> ret(x.size());
		function<void(int, int, Poly, int)> solve = [&] (int l, int r, Poly f, int u) -> void {
			if (l == r) {
				ret[l] = f[0];
				return;
			}
			int mid = (l + r) / 2;
			solve(l, mid, (f % tmp[ls(u)]).second, ls(u));
			solve(mid + 1, r, (f % tmp[rs(u)]).second, rs(u));
		};
		solve(0, x.size() - 1, (*this % tmp[1]).second, 1);
		return ret;
	}
};
int N, M, S, mxd, vis[MAXN], sz[MAXN], A[MAXN], B[MAXN];
long long cnt[MAXN];
vector<int> v[MAXN];
Poly buc;
int getrt(int nw, int fa) {
	sz[nw] = 1;
	int mx = 0;
	for (auto &j: v[nw]) {
		if (j != fa && !vis[j]) {
			int t = getrt(j, nw);
			if (t) {
				return t;
			}
			sz[nw] += sz[j];
			mx = max(mx, sz[j]);
		}
	}
	if (max(mx, S - sz[nw]) <= S / 2) {
		return nw;
	}
	return 0;
}
void get_sz(int nw, int fa, int dep) {
	sz[nw] = 1;
	if (dep >= buc.v.size()) {
		buc.v.push_back(1);
	}
	else {
		++buc[dep];
	}
	for (auto &j: v[nw]) {
		if (!vis[j] && fa != j) {
			get_sz(j, nw, dep + 1);
			sz[nw] += sz[j];
		}
	}
}
void calc(int x) {
	buc.Squ();
	if (x > 0) {
		for (int i = 0; i < buc.len(); ++i) {
			cnt[i] += buc[i];
		}
	}
	else {
		for (int i = 0; i < buc.len(); ++i) {
			cnt[i] -= buc[i];
		}
	}
	buc.clear();
}
void solve(int nw) {
	vis[nw] = 1;
	mxd = 0;
	get_sz(nw, 0, 0);
	calc(1);
	for (auto &j: v[nw]) {
		if (!vis[j]) {
			mxd = 0;
			buc.v.push_back(0);
			get_sz(j, 0, 1);
			calc(-1);
			S = sz[j];
			solve(getrt(j, 0));
		}
	}
}
int main() {
	freopen("trade.in", "r", stdin);
	freopen("trade.out", "w", stdout);
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> N >> M;
	S = N;
	for (int i = 1, x, y; i < N; ++i) {
		cin >> x >> y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	solve(getrt(1, 0));
	Poly dis;
	dis.set(N);
	for (int i = 0; i < N; ++i) {
		dis[i] = cnt[i] % MOD;
	}
	vector<int> p;
	for (int i = 1, x, y; i <= M; ++i) {
		cin >> A[i] >> B[i] >> x >> y;
		p.push_back(Mod(1 - (long long) x * Ksm(y, MOD - 2) % MOD));
	}
	p = dis.getmulpointvalue(p);
	for (int i = 1, inv = Ksm((long long) N * N % MOD, MOD - 2); i <= M; ++i) {
		cout << Mod((long long) B[i] * p[i - 1] % MOD * inv % MOD - A[i]) << '\n';
	}
	return 0;
}

密码(password)

题意

通信题

给你一个 \(n\) 长度的 \(01\)\(a\),然后你要返回一个 \(m\) 长度的 \(01\)\(b\),然后强制固定 \(k\) 个位置的 \(0\)\(1\),然后你还要能给你一个 \(m\) 个长度的 \(01\) 串,你能把 \(n\) 的那个串返回出来,但是不告诉你 \(k\) 个位置都是啥。

题解

我们考虑,随机 \(m\)\(n\) 长度的 \(01\) 串,然后那个 \(m\) 个串的每个 \(01\) 位就表示异或不异或第 \(i\)\(n\) 长度的 \(01\) 串。

解码的时候就直接异或出来就是原来的串。

编码的时候,我们要根据这 \(m\) 个随机串,能异或出来我们原来的 \(a\) 串,首先,我们令异或的答案是 \(g\),然后 \(g\) 首先要搞上那 \(k\) 个位置里是 \(1\)\(01\) 串,那么,我们要把剩下这 \(g\) 改成 \(a\),我们只要把剩下的 \(m-k\)\(01\) 串建立一个线性基,然后按位构造就好了。

我们考虑这样的成功概率,自然就是 \(m\)\(n\)\(01\) 向量满秩的概率,然后假定存在一组 \(n\) 个不相关 \(n\) 维向量的概率。

考虑正难则反,算不存在的概率,

这个玩意不好算,但是我们可以算出来他的上界。

应该是 \(\prod_{i=1}^N{(1-\frac{1}{2^i})}\times (\frac{1}{2^{50}})\)。这玩意很接近 \(0\) 了,那么成功的概率就大概是 \(1\)

#include "password.h"
#include <bits/stdc++.h>
using std::bitset;
const long long s1 = 292828, s2 = 2836372, s3 = 998244353;
long long seed;
inline long long mrand() {
	return seed = (seed * s1 + s2 + rand()) % s3;
}
bitset<1000> d[1010];
bitset<2050> p[1010];
void encoder(int _n, int m, int k, const char *a, const char *b, char *ans) {
	srand(20130508);
	bitset<1000> f, g;
	bitset<2050> e;
	seed = 0;
	f.reset();
	g.reset();
	e.reset();
	mrand();
	for (int i = 0; i < _n; ++i) {
		g[i] = a[i] - '0';
	}
	for (int i = 0; i < m; ++i) {
		for (int j = 0; j < _n; ++j) {
			f[j] = mrand() & 1;
		}
		if (b[i] == '0') {
			continue;
		}
		else if (b[i] == '1') {
			g ^= f;
		}
		else {
			e[i] = 1;
			std::function<void(bitset<1000>, bitset<2050>)> solve = [&] (bitset<1000> a, bitset<2050> b) {
				for (int i = _n - 1; ~i; --i) {
					if (a[i]) {
						if (d[i][i]) {
							a ^= d[i];
							b ^= p[i];
						}
						else {
							d[i] = a;
							p[i] = b;
							break;
						}
					}
				}
				return;
			};
			solve(f, e);
			e[i] = 0;
		}
	}
	bitset<2050> h;
	h.reset();
	for (int i = 0; i < m; ++i) {
		h[i] = b[i] == '1';
	}
	for (int i = _n - 1; ~i; --i) {
		if (g[i]) {
			g ^= d[i];
			h ^= p[i];
		}
	}
	for (int i = 0; i < m; ++i) {
		ans[i] = '0' + h[i];
	}
}
void decoder(int _n, int m, const char *a, char *ans) {
	srand(20130508);
	seed = 0;
	mrand();
	bitset<1000> b, c;
	b.reset();
	c.reset();
	for (int i = 0; i < m; ++i) {
		for (int j = 0; j < _n; ++j) {
			b[j] = mrand() & 1;
		}
		if (a[i] == '1') {
			c ^= b;
		}
	}
	for (int i = 0; i < _n; ++i) {
		ans[i] = '0' + c[i];
	}
	return;
}
posted @ 2022-01-26 09:53  siriehn_nx  阅读(81)  评论(4编辑  收藏  举报