20250203
T1
情绪波动
可以搜状态,也可以暴力跑出前若干项后高消出递推系数。第一种容易挂,第二种更方便,但是场上写的第一种。
代码
#include <iostream>
#include <string.h>
#include <cassert>
#include <time.h>
#define int long long
using namespace std;
const int P = 998244353;
inline void Madd(int& x, int y) { (x += y) >= P ? (x -= P) : 0; }
int f[505][505];
int a[5], cnt = 0, N;
bool A[10][10];
int mp[200005], _mp[200005];
inline bool bet(int l, int x, int r) { return l <= x && x <= r; }
int S[19][4][2] = {
{
{ 0, 0 },
{ 1, 0 },
{ 2, 0 },
{ 3, 0 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ 0, 3 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ 1, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ -1, 1 },
{ 1, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ -1, 1 }
},
{
{ 0, 0 },
{ 1, 0 },
{ 2, 0 },
{ 1, 1 }
},
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ -1, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 1, 1 },
{ 1, 2 }
},
{
{ 0, 0 },
{ 1, 0 },
{ 1, 1 },
{ 2, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ -1, 1 },
{ -1, 2 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 1, 0 },
{ 1, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ -1, 2 }
},
{
{ 0, 0 },
{ 1, 0 },
{ 2, 0 },
{ 2, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ 1, 0 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 1, 1 },
{ 2, 1 }
},
{
{ 0, 0 },
{ 1, 0 },
{ 1, 1 },
{ 1, 2 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 0, 2 },
{ 1, 2 }
},
{
{ 0, 0 },
{ 0, 1 },
{ -1, 1 },
{ -2, 1 }
},
{
{ 0, 0 },
{ 0, 1 },
{ 1, 0 },
{ 2, 0 }
}
};
struct Matrix {
int a[105][105];
Matrix() { memset(a, 0, sizeof a); }
int* operator[](int x) { return a[x]; }
} I, T, pw[105];
Matrix operator*(Matrix a, Matrix b) {
Matrix c;
for (int i = 0; i <= N; i++) {
for (int j = 0; j <= N; j++) {
for (int k = 0; k <= N; k++)
Madd(c[i][j], a[i][k] * b[k][j] % P);
}
}
return c;
}
Matrix operator&(Matrix a, Matrix b) {
Matrix c;
for (int j = 0; j <= N; j++) {
for (int k = 0; k <= N; k++)
Madd(c[0][j], a[0][k] * b[k][j] % P);
}
return c;
}
int st;
bool chkfill(int x, int sx, int sy) {
bool ok = 1;
for (int i = 0; i < 4; i++) ok &= (bet(1, sx + S[x][i][0], 4) && bet(0, sy + S[x][i][1], 4) && A[sx + S[x][i][0]][sy + S[x][i][1]] == 0);
if (!ok)
return 0;
for (int i = 0; i < 4; i++) A[sx + S[x][i][0]][sy + S[x][i][1]] = 1;
return 1;
}
void un(int x, int sx, int sy) {
for (int i = 0; i < 4; i++) A[sx + S[x][i][0]][sy + S[x][i][1]] = 0;
}
int H(int sy = 0) {
int ret = 0;
for (int i = 1; i <= 4; i++) {
for (int j = 0; j < 3; j++)
ret |= (A[i][sy + j] << ((i - 1) * 4 + j));
}
return ret;
}
void dfs(int x) {
if (x == 5) {
int d = H(1);
if (mp[d])
++f[mp[st]][mp[d]];
}
if (A[x][0])
dfs(x + 1);
for (int i = 0; i < 19; i++) {
if (chkfill(i, x, 0)) {
dfs(x + 1);
un(i, x, 0);
}
}
}
bool ok[5][5];
void chk(int x, int y) {
if (ok[x][y])
return;
ok[x][y] = 1;
if (x != 1 && A[x - 1][y])
chk(x - 1, y);
if (x != 4 && A[x + 1][y])
chk(x + 1, y);
if (y != 0 && A[x][y - 1])
chk(x, y - 1);
if (y != 2 && A[x][y + 1])
chk(x, y + 1);
}
void chk2(int x, int y) {
if (ok[x][y])
return;
ok[x][y] = 1;
if (x != 1 && !A[x - 1][y])
chk2(x - 1, y);
if (x != 4 && !A[x + 1][y])
chk2(x + 1, y);
if (y != 0 && !A[x][y - 1])
chk2(x, y - 1);
if (y != 2 && !A[x][y + 1])
chk2(x, y + 1);
}
void dfs(int x, int y, int s = 0, bool flag = 0) {
if (y == 3) {
if (s % 4 != 0)
return;
st = H();
memset(ok, 0, sizeof ok);
bool lg = 1;
for (int i = 1; i <= 4; i++) A[i][0] ? chk(i, 0) : void();
for (int i = 1; i <= 4; i++) !A[i][2] ? chk2(i, 2) : void();
for (int i = 1; i <= 4; i++) {
for (int j = 0; j < 3; j++)
lg &= (ok[i][j]);
}
if (!lg)
return;
if (flag)
dfs(1);
else {
++cnt;
mp[_mp[cnt] = st] = cnt;
}
return;
}
dfs(x % 4 + 1, y + (x == 4), s, flag);
A[x][y] = 1;
dfs(x % 4 + 1, y + (x == 4), s + 1, flag);
A[x][y] = 0;
}
int dp[1005][105];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int ttt = clock();
freopen("fudepen.in", "r", stdin);
freopen("fudepen.out", "w", stdout);
dfs(1, 0);
N = cnt;
dfs(1, 0, 0, 1);
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= cnt; j++)
T[i][j] = f[i][j];
}
pw[0] = T;
for (int i = 1; i <= 35; i++) pw[i] = pw[i - 1] * pw[i - 1];
int tc;
cin >> tc;
while (tc--) {
int n;
cin >> n;
memset(I.a, 0, sizeof I.a);
I[0][1] = 1;
for (int i = 0; i <= 35; i++) {
if (n & (1ll << i))
I = I & pw[i];
}
cout << I[0][1] << "\n";
}
cerr << (1.0 * clock() - ttt) / CLOCKS_PER_SEC << "\n";
return 0;
}
T2
线性基
前一半组合数奇偶前缀和可以通过观察杨辉三角转化为上一行的组合数前缀和,然后组合数前缀和用莫队搞掉,莫队指针移动时当前值的维护也是观察杨辉三角可以得到。后一半 FWT 分治乘,每个区间根据当前选的数的奇偶性维护两个多项式即可。这样是 \(\mathcal{O}(2^mm^2)\)。也可以通过按位 dp 做到 \(\mathcal{O}(2^mm)\)。具体地,考虑 \(ans = \text{IFWT}(\prod\limits_i\text{FWT}(a_i + b_ix^i))\),其中乘是点乘。然后设 \(w_{i, 0} = a_i + b_i, w_{i, 1} = a_i - b_i\),则根据定义,\([x^j]\text{FWT}(a_i + b_ix^i) = w_{i, popcount(i \& j) \& 1}\)。我们只需要求这些东西的乘积。下设 \(p(i) = popcount(i) \& 1\)。
类似 FWT,从低位向高位考虑,\(f_{i, j}\) 表示考虑了前 \(i\) 位,所有与 \(j\) 高 \(m - i\) 位相同的数 \(k\) 对 \(j\) 的贡献(\(w_{k, p(k \& j)}\))之积,\(g_{i, j}\) 表示所有这样的 \(k\) 的 \(w_{k, p(k \& j) \oplus 1}\) 之积。然后转移就是容易做的。最后 IFWT 回去。复杂度就是 \(\mathcal{O}(2^mm)\)。
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <array>
#define int long long
using namespace std;
const int P = 998244353, i2 = (P + 1) / 2;
inline void Madd(int& x, int y) { (x += y) >= P ? (x -= P) : 0; }
inline void Mmul(int& x, int y) { x *= y, x %= P; }
int fac[2000005], ifac[2000005], inv[2000005];
void Cpre(int n) {
fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;
for (int i = 2; i <= n; i++) {
fac[i] = fac[i - 1] * i % P;
inv[i] = (P - P / i) * inv[P % i] % P;
ifac[i] = ifac[i - 1] * inv[i] % P;
}
}
inline int C(int n, int m) { return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] % P * ifac[n - m] % P; }
void FWT(int* f, int n, int opt = 1) {
for (int i = 1; i < n; i <<= 1) {
for (int j = 0; j < n; j += (i << 1)) {
for (int k = 0; k < i; k++) {
int x = f[j | k], y = f[i | j | k];
f[j | k] = (x + y) % P;
f[i | j | k] = (x - y + P) % P;
}
}
}
if (opt != 1)
for (int i = 0; i < n; i++) f[i] = f[i] * inv[n] % P;
}
int n, k;
int a[2000005], b[2000005];
int qcnt, B = 350;
struct qquery {
int n, k, x, bn, bk;
void operator()() { bn = n / B, bk = k / B; }
} qs[2000005];
int w[2000005][2];
int dp[1050005][21][2];
int f[2000005];
signed main() {
freopen("base.in", "r", stdin);
freopen("base.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
Cpre(2000000);
cin >> n;
for (int i = 0; i < (1 << n); i++) cin >> a[i];
for (int i = 0; i < (1 << n); i++) {
cin >> b[i];
qs[++qcnt] = ((qquery) { a[i], b[i], i });
if (a[i] == 0)
w[i][1] = 1;
else if (b[i] & 1)
w[i][1] = (P - C(a[i] - 1, b[i])) % P;
else
w[i][1] = C(a[i] - 1, b[i]);
}
for (int i = 1; i <= qcnt; i++) qs[i]();
sort(qs + 1, qs + qcnt + 1, [](qquery a, qquery b) { return (a.bn == b.bn) ? ((a.bn & 1) ? (a.k < b.k) : (a.k > b.k)) : (a.n < b.n); });
for (int i = 1, cn = 1, ck = 0, ca = 1; i <= qcnt; i++) {
while (cn < qs[i].n) ca = (ca * 2 - C(cn++, ck) + P) % P;
while (ck > qs[i].k) Madd(ca, P - C(cn, ck--));
while (cn > qs[i].n) ca = (ca + C(--cn, ck)) * i2 % P;
while (ck < qs[i].k) Madd(ca, C(cn, ++ck));
w[qs[i].x][0] = ca;
// cout << ca << "\n";
}
for (int i = 0; i < (1 << n); i++) {
for (int j = 0; j <= n; j++)
dp[i][j][0] = dp[i][j][1] = 1;
}
for (int i = 0; i < (1 << n); i++) dp[i][0][0] = w[i][0], dp[i][0][1] = w[i][1];
for (int j = 0; j < n; j++) {
for (int i = 0; i < (1 << n); i++) {
Mmul(dp[i ^ (1 << j)][j + 1][0], dp[i][j][0]);
Mmul(dp[i][j + 1][(i >> j) & 1], dp[i][j][0]);
Mmul(dp[i ^ (1 << j)][j + 1][1], dp[i][j][1]);
Mmul(dp[i][j + 1][!((i >> j) & 1)], dp[i][j][1]);
}
}
for (int i = 0; i < (1 << n); i++) f[i] = dp[i][n][0];
FWT(f, 1 << n, 0);
for (int i = 0; i < (1 << n); i++) cout << f[i] << " ";
cout << "\n";
return 0;
}
搜状态 \(\rightarrow\) 暴力求出前几项后高消出转移系数。
类似 FWT 的按位 dp。

浙公网安备 33010602011771号