2025.2

2025.2

1. CF1091F - New Year and the Mallard Expedition

https://www.luogu.com.cn/problem/CF1091F

显然答案为所有 \(b-a-1,c-b-1\) 的 sg 异或和。问题变为怎样求 sg。

发现 sg 式子十分公式,形如 \(sg_i=\operatorname{xor}\{sg_{i-a_j}\}\),这种式子在值域较小时可以使用 bitset 优化。设 \(p_{i,j}\) 表示是否存在一个 \(sg_k=j\) 使得 \(k\) 能贡献到 \(i\),那么这个时候就简单了:

  • 更新,则令 p[sg_k] |= k << ok;
  • 查询,只用查询所有 \(p\) 的第 \(i\) 位即可。

复杂度 \(O(nV+\dfrac{n^2}w)\),时间复杂度 \(O(\dfrac{nV}w)\)

点击查看代码
// Problem: H. New Year and the Tricolore Recreation
// Contest: Codeforces - Good Bye 2018
// URL: https://codeforces.com/problemset/problem/1091/H
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5 + 10;
int mx;
int n, b, f[N];
int p[N], v[N], pc, isp[N];
bitset<N> ok, sg[70];

int main(){
	mx = N - 10;
	for(int i = 2; i <= mx; ++ i){
		if(!v[i]){
			p[++pc] = i;
			isp[i] = 1;
			ok.set(i);
		}
		for(int j = 1; j <= pc && i * p[j] <= mx; ++ j){
			v[i*p[j]] = 1;
			if(i % p[j] == 0){
				break;
			}
		}
	}
	for(int i = 1; i <= pc; ++ i){
		for(int j = 1; j <= pc && p[i] * p[j] <= mx; ++ j){
			ok.set(p[i]*p[j]);
		}
	}
	scanf("%d%d", &n, &b);
	ok.set(b, 0);
	for(int i = 0; i <= mx; ++ i){
		int val = 0;
		for(int j = 0; j < 70; ++ j){
			if(!sg[j].test(i)){
				val = j;
				break;
			}
		}
		f[i] = val;
		sg[val] |= ok << i;
	}
	int ans = 0;
	while(n--){
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		ans ^= f[b-a-1] ^ f[c-b-1];
	}
	puts(ans ? "Alice\nBob" : "Bob\nAlice");
	return 0;
}

2. qoj4228 - Double Sort

https://qoj.ac/contest/943/problem/4228

不考虑这个输出小数。来点 \(O(n^2+m\log m)\) 做法。

原题意可以转化为:对于所有 \(\sum b\leq m\) 的正整数序列,对于每一个 \(i\) 求从小到大排序后第 \(i\) 项在所有方案中的和。

\[\sum_{b}b_{n-i+1} = \sum_b\sum_{k=1}^m[b_{n-i+1}\geq k]=\sum_{k=1}^mf(i,k) \]

其中 \(f(i,k)\) 表示至少 \(i\) 个数 \(\geq k\) 的方案数。

如何计算这个 \(f\)?考虑设 \(g(i,k)\) 表示恰好 \(i\) 个数 \(\geq k\) 的方案数;\(h(i,k)\) 表示选定 \(1\sim i\) 下标上的数 \(\geq k\) 的方案数。

根据定义有:\(f(i,k)=\sum_{j=i}^n g(j,k)\)

根据二项式反演有:\(g(j,k) = \sum_{p=j}^n \dbinom nph(p,k)\dbinom{p}{j}(-1)^{p-j}\)

根据插版法有:\(h(p,k)=\sum_{x=1}^m\dbinom{x-p(k-1)-1}{n-1} = \dbinom{x-p(k-1)}n\)

\(f(i,k)\)

\[\begin{aligned} f(i,k)&=\sum_{j=i}^n g(j,k)\\ &=\sum_{j=i}^n\sum_{p=j}^n h(p,k)\dbinom pj (-1)^{p-j}\\ &=\sum_{p=i}^n\dbinom nph(p,k)(-1)^{p-i}\sum_{j=i}^p\dbinom pj (-1)^{j-i}\\ &=\sum_{p=i}^n\dbinom nph(p,k)(-1)^{p-i}\dbinom{p-1}{i-1} \end{aligned}\]

最后一步可以在杨惠三角上直接证明。

带入,得:

\[\begin{aligned}\sum_{b}b_{n-i+1} &=\sum_{k=1}^mf(i,k)\\ &=\sum_{p=i}^n\dbinom np (-1)^{p-i}\dbinom {p-1}{i-1}\sum_{k=1}^m h(p,k) \end{aligned}\]

由于 \(h(p,k)\neq 0\) 的必要条件是 \(pk \leq m\),所以只用算 \(O(m\log m)\) 次。总复杂度 \(O(n^2+m\log m)\)

3. LuoguP4707 - 重返现世

https://www.luogu.com.cn/problem/P4707

一直想写这个题,今天写一下,发现写起来很简单。

首先要知道扩展 min-max 容斥式子:

\[\operatorname{kthmax}(S)=\sum_{T\subseteq S,T\neq \emptyset}\dbinom{|T|-1}{k-1}(-1)^{|T|-k}\min(T) \]

题面求的是第 \(n-k+1\) 大的期望(下文中的 \(k\)\(n-k+1\))。那么可以转化为求若干最小期望。这个是好求的,集合 \(S\) 内的最小期望为 \(\dfrac{m}{\sum_{i\in S}p_i}\)

容易列出 dp:设 \(f_{i,j}\) 表示集合中有 \(i\) 个数,\(p\) 和为 \(j\) 的方案数,复杂度 \(O(n^2m)\)

考虑利用上 \(k\) 很小(即原题面中的 \(|n-k|\leq 10\)),考虑将这个组合数利用组合意义拆开来:即 \(i\) 个数中选定 \(k\) 个数,要求第一个数必选。

那么新的 dp 状态就是:设 \(f_{i,j}\) 表示和为 \(i\),且已经选了 \(j\) 个数,方案数乘上 \(-1\) 的选的数的个数次方。对于每个 \(p\)\(p\) 在集合中的转移有两条(按照背包 dp 的方式转移,\(p\) 不在集合中的不用考虑):

  • \(f_{i,j} \leftarrow -f_{i-p,j}\),表示 \(p\) 放入,但是不选;要求 \(i-p\neq 0\)(即不是第一个数);
  • \(f_{i,j}\leftarrow -f_{i-p,j-1}\),表示 \(p\) 放入且选;要求 \(j>0\)(显然)。

初值 \(f_{0,0}=(-1)^k\),答案为 \(\sum\dfrac mi f_{i,k}\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll P = 998244353;
int f[10010][13];
int n, k, m, p[1010];

ll qp(ll x, ll y){
	ll ans = 1;
	while(y){
		if(y & 1){
			ans = ans * x % P;
		}
		x = x * x % P;
		y >>= 1;
	}
	return ans;
}

int main(){
	f[0][0] = 1;
	scanf("%d%d%d", &n, &k, &m);
	k = n - k + 1;
	for(int i = 1; i <= n; ++ i){
		scanf("%d", &p[i]);
		for(int v = m; v >= p[i]; -- v){
			for(int c = 0; c <= k; ++ c){
				if(c){
					f[v][c] -= f[v-p[i]][c-1];
					if(f[v][c] < 0){
						f[v][c] += P;
					}
				}
				if(v > p[i]){
					f[v][c] -= f[v-p[i]][c];
					if(f[v][c] < 0){
						f[v][c] += P;
					}
				}
			}
		}
	}
	ll ans = 0;
	for(int i = 1; i <= m; ++ i){
		ans += qp(i, P-2) % P * f[i][k] % P;
		ans %= P;
	}
	printf("%lld\n", ans * qp(P-1, k) % P * m % P);
	return 0;
}
posted @ 2025-01-30 14:47  KiharaTouma  阅读(110)  评论(0)    收藏  举报