闲话 22.10.3
闲话
最近有一件事我比较在意。
某一日 EI 发了一个犇犇
众所周知,这是《四重罪孽》里的一句歌词
然后某些中V厨就按耐不住自己手头的歌词了
回想初二在家的那段时间
一套作业
带上耳机循环一天《妄想症》
的时光
让我现在还能唱一点四重罪孽
但是现在早就不记得词曲了 下次回家听听吧
夢の続きを知りたいのかい?
誰も見たこと無い絵本を
捲りなさい
それがあなたの
望む世界だとしよう
夢の終わりで眠ればいい
杂题
称一个长度为 \(n\) 的序列 \(a\) 是 \(\text{PalindORme}\) 的,当且仅当对于任意 \(1 \le i \le n\),满足 \(a_1 | a_2 | \dots | a_i = a_n | a_{n-1} | \dots |a_{n-i+1}\),其中
|
表示按位或运算。称一个长度为 \(n\) 的序列 \(b\) 是 \(\text{good}\) 的,当且仅当它可以重排成一个 \(\text{PalindORme}\) 的序列。给你 \(n,k,m\),求长度为 \(n\),每个元素值域为 \([0,2^k)\) 的序列中有多少个是 \(\text{good}\) 的,对 \(m\) 取模。
\(n,k\le 80\)。
读题。读完了可以发现合法序列是关于中心对称的。具体地,从两边开始分别向中间镜像地拓展一个数,每次加入的二进制位是相同的。
然后刻画一下这个性质:
首先整一个计数器 \(V = 0\)。每次选择这个序列两端的数 \(x,y\),一定满足 \(x | V = y | V\)。赋 \(V \leftarrow x|V\),再把这两个数从序列中删除。
由于需要计数的串是重排之后的,因此对于 \(\text{good}\) 的序列,可以通过若干次操作变为长度不超过 1 的序列:
仍然是初始为 0 的计数器,每次选择一对任意位置的数 \(x,y\) 满足 \(x | V = y | V\),赋 \(V \leftarrow x|V\),随后把这两个数删除。
对于不 \(\text{good}\) 的序列,定义它的最优子序列为通过上述操作移除的序列。可以发现在移除最优子序列后,不 \(\text{good}\) 的序列剩余元素都满足 xor \(V\) 不为 0 且之间两两不同。
因此一个不 \(\text{good}\) 的序列能够生成一个长度小于它的 \(\text{good}\) 的序列。
考虑一个套路容斥。
\(f_{i,j}\) 表示长度为 \(i\),二进制位数量为 \(j\) 的序列数,\(g_{i,j}\) 表示长度为 \(i\),二进制位数量为 \(j\) 的不合法序列数,\(h_{i,j}\) 表示长度为 \(i\),二进制位数量为 \(j\) ,内部元素均不为 0 且两两不同的序列数。
由定义,可以得到 \(f_{i,j}\) 的计算方式:
\(h_{i,j}\) 的计算式也可类似地得到:
然后可以枚举最优子序列长度和二进制位个数来得到 \(g_{i,j}\)。
意义:我们首先钦定最优子序列的长度 \(x\) 和位数 \(y\),这部分的总种类数是 \(\binom{i}{x} \binom{j}{y}\) 的。对于长度为 \((i-x)\) 的每个剩余元素,能任意填入最优子序列中已经存在的 \(y\) 位,这部分的总种类数是 \((2^y)^{i-x}\) 的。由于剩余元素都满足 xor \(V\) 不为 0 且之间两两不同,这部分的种类数是 \(h_{i-x,j-y}\) 的。最后考虑最优子序列的种类数,为 \((f_{x,y} - g_{x,y})\)。求积再求和即为上式。
然后递推就完了。
总时间复杂度 \(O(n^2k^2)\)。
code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
const int N = 90 + 10;
int n, k, mod, pw[N * N], C[N][N], f[N][N], g[N][N], h[N][N], ans;
typedef long long ll; typedef __int128 lll;
struct FastMod { int m; ll b; void init(int _m) { m = _m; b = ((lll)1<<64) / m; } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod;
int add(int a, int b) { return (a += b) >= mod ? a - mod : a; }
int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }
int L(int n, int m) { int ret = 1; rep(i,0,m-1) ret = mul(ret, n - i + mod); return ret; }
void init(int bnd) {
Mod.init(mod);
rep(i,0,bnd) C[i][0] = 1;
pw[0] = 1;
rep(i,1,bnd) {
rep(j,1,i) C[i][j] = add(C[i-1][j-1], C[i-1][j]);
}
rep(i,1,n*k) pw[i] = add(pw[i-1], pw[i-1]);
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> k >> mod;
init(max(n, k));
rep(i,0,n) rep(j,0,k) rep(l,0,j) {
f[i][j] = add(f[i][j], mul(((j - l) & 1) ? mod - C[j][l] : C[j][l], pw[l * i]));
h[i][j] = add(h[i][j], mul(((j - l) & 1) ? mod - C[j][l] : C[j][l], L(pw[l] - 1, i)));
}
rep(i,1,n) rep(j,1,k) rep(i2,0,i-1) rep(j2,0,j-1)
if ((n & 1) and (i == n) and (i2 == n-1)) continue;
else g[i][j] = add(g[i][j], mul(C[i][i2], C[j][j2], pw[j2 * (i - i2)], h[i - i2][j - j2], f[i2][j2] + mod - g[i2][j2]));
rep(i,0,k) ans = add(ans, mul(C[k][i], f[n][i] + mod - g[n][i]));
cout << ans << endl;
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat221003.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。