杂题选做

[ARC107D] Number of Multisets

妙妙递推题。

题意:求将 \(K\) 分拆成 \(N\) 个形如 \(\dfrac{1}{2^n}\) 形式的数的方案数。

\(f_{i,j}\) 表示选了 \(i\) 个数,和为 \(j\) 的方案数。边界条件为 \(f_{0,0}=1\)。两种转移:

  • \(i\) 个数选 \(1\),即 \(f_{i-1,j-1}\)
  • \(i\) 个数不选 \(1\),直接做难以统计,但发现这样的选法和全部翻倍之后的选法是一一对应的,这样的方案数为 \(f_{i,2j}\)

故转移方程为 \(f_{i,j}=f_{i-1,j-1}+f_{i,2j}\)

就是重复利用 dp 数组的思想,发现两个状态的方案集合是一一对应的就可以直接利用。

#include <bits/stdc++.h>
using namespace std;
const int N = 3005, p = 998244353;
int n, k, f[N][N];
int main() {
	cin >> n >> k;
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j >= 1; j--) f[i][j] = (f[i - 1][j - 1] + (j << 1 <= i ? f[i][j << 1] : 0)) % p;
	}
	cout << f[n][k];
}

CF1305F Kuroni and the Punishment

神秘随机化题。

答案不会大于 \(n\),因为可以把所有数都变成偶数。

直觉上来看,操作次数不大于 \(n\) 意味着不能有太多数操作次数大于 \(1\) 次。更准确地说,至少有 \(\dfrac{n}{2}\) 个数至多只被操作 \(1\) 次。

随机化,枚举 \(a-1\)\(a\)\(a+1\) 的所有因数并统计答案。如果随机次数为 \(t\),则正确率为 \(1-\dfrac{1}{2^t}\)

找操作次数上界、找到占比至少为 xxx 的部分然后考虑随机化

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n, a[N], T = 33, ans;
mt19937 rnd(time(0));
void chkmin(int x) {
	for (int i = 2; i * i <= x; i++) {
		if (x % i) continue;
		int tmp = 0;
		for (int j = 1; j <= n; j++) tmp += a[j] >= i ? min(a[j] % i, i - a[j] % i) : i - a[j];
		ans = min(ans, tmp);
		while (!(x % i)) x /= i;
	}
	if (x > 1) {
		int tmp = 0;
		for (int j = 1; j <= n; j++) tmp += a[j] >= x ? min(a[j] % x, x - a[j] % x) : x - a[j];
		ans = min(ans, tmp);
	}
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i], ans += (a[i] & 1);
	while (T--) {
		int x = a[rnd() % n + 1];
		chkmin(x - 1), chkmin(x), chkmin(x + 1);
	}
	cout << ans;
}

CF1140E Palindrome-less Arrays

萌萌计数题。

没有长度为奇数的回文子串等价于不存在 \(\mathtt{aba}\) 这样的子串,意味着 \(\forall 1\leq i\leq n-2,a_i\not=a_{i+2}\)。将奇数下标和偶数下标分别提出来考虑即可。

对于一段长度为 \(x\) 的连续的 \(-1\),若其两端都没有限制,则方案数为 \(k(k-1)^{x-1}\);若只有一端有限制,则方案数为 \((k-1)^x\);否则分两端的限制是否相同来讨论,设 \(f_{x,0/1}\) 表示限制不同/相同的方案数,有:

\[\begin{aligned} f_{x,0}&=(k-2)f_{x-1,0}+f_{x-1,1}\\ f_{x,1}&=(k-1)f_{x-1,0} \end{aligned} \]

注意特判不合法的情况,即已经存在固定的相邻的相同数。

贡献独立的话,将同余类提出来单独做。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200005, p = 998244353;
int n, k, a[N], t, b[N], f[N][2], ans = 1;
int qpow(int a, int b) {
	int c = 1;
	while (b) { if (b & 1) c = (ll)c * a % p; a = (ll)a * a % p, b >>= 1; }
	return c;
}
int solve() {
	for (int i = 1; i < t; i++) if (~b[i] && b[i] == b[i + 1]) return 0;
	int ans = 1;
	for (int l = 1, r; l <= t; l = r + 1) {
		while (l <= t && ~b[l]) l++;
		r = l;
		while (r <= t && !~b[r]) r++;
		if (l > --r) continue;
		if (l == 1 && r == t) ans = (ll)ans * k % p * qpow(k - 1, t - 1) % p;
		else if (l == 1 || r == t) ans = (ll)ans * qpow(k - 1, r - l + 1) % p;
		else ans = (ll)ans * f[r - l + 1][b[l - 1] == b[r + 1]] % p;
	}
	return ans;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> k;
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) f[i][0] = ((ll)f[i - 1][0] * (k - 2) + f[i - 1][1]) % p, f[i][1] = (ll)f[i - 1][0] * (k - 1) % p;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i += 2) b[++t] = a[i];
	ans = (ll)ans * solve() % p;
	t = 0;
	for (int i = 2; i <= n; i += 2) b[++t] = a[i];
	ans = (ll)ans * solve() % p;
	cout << ans;
}

[ARC104F] Visibility Sequence

不是很难 dp 题。

显然大于 \(n\)\(a\) 没有用,和 \(n\) 取 min 就行,那么时间复杂度应该只和 \(n\) 有关,比如 \(\mathcal O(n^4)\)

\(P\) 是单调栈,它和笛卡尔树一一对应(如果有多个最大值选择最后一个),考虑区间 dp,令 \(p\) 为区间 \([l,r]\) 内最后一个最大值的位置,值为 \(h_p\),那么 \([l,p-1]\) 内的数都要 \(\le h_p\)\([p+1,r]\) 内的数都要 \(<h_p\)

\(f_{l,r,x}\) 表示区间 \([l,r]\) 内的数都不超过 \(x\) 的方案数,边界条件为 \(f_{i,i+1,j}=1\)。枚举最后一个最大值的位置,有:

\[f_{l,r,x}=\sum_{p=l}^rf_{l,p-1,\min(x,a_p)}f_{p+1,r,\min(x,a_p)-1} \]

单调栈 \(\leftrightarrow\) 笛卡尔树 \(\rightarrow\) 区间 dp

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105, mod = 1e9 + 7;
int n, a[N], f[N][N][N];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 0; i <= n; i++) for (int j = 0; j <= n; j++) f[i + 1][i][j] = 1;
	for (int l = n; l >= 1; l--) {
		for (int r = l; r <= n; r++) {
			for (int x = 1; x <= n; x++) {
				for (int p = l; p <= r; p++) f[l][r][x] = (f[l][r][x] + (ll)f[l][p - 1][min(x, a[p])] * f[p + 1][r][min(x, a[p]) - 1]) % mod;
			}
		}
	}
	cout << f[1][n][n];
}

[ARC044B] 最短路問題

分层图,设 \(a_i=h\) 的有 \(b_h\) 个点,考虑层内连边和向上一层的连边,则答案为:

\[\prod_{i=1}^{n-1}2^{\binom{b_i}{2}}\left(2^{b_{i-1}}-1\right)^{b_i} \]

最短路类图计数,考虑分层图。

更多此类题目:ABC389G

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005, p = 1e9 + 7;
int n, a, b[N], ans = 1;
int qpow(int a, ll b) {
	int c = 1; b %= p - 1;
	while (b) { if (b & 1) c = (ll)c * a % p; a = (ll)a * a % p, b >>= 1; }
	return c;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a, b[a]++;
		if (i == 1 && a != 0 || i != 1 && a == 0) return cout << 0 << '\n', 0;
	}
	for (int i = 1; i < n; i++) ans = (ll)ans * qpow(2, (ll)b[i] * (b[i] - 1) / 2) % p * qpow(qpow(2, b[i - 1]) - 1, b[i]) % p;
	cout << ans << '\n';
}
posted @ 2025-02-12 15:50  Pentimentqwq  阅读(19)  评论(0)    收藏  举报