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。

posted @ 2025-02-09 16:35  forgotmyhandle  阅读(26)  评论(0)    收藏  举报