「解题报告」ARC132F Takahashi The Strongest

不会 FWT 的真实。需要重写篇 FWT 博客。

考虑 Takahashi 获胜当且仅当 Aoki 和 Snuke 选的相同,Takahashi 选的是它的下一个。

至少有一个相等,可以容斥为所有的都不相等。

那么相当于两个数能够给一个集合造成贡献。比如 \(R, R \to \{R, S\}\)\(R, P \to \{R, P, S\}\)

发现只有四种集合,我们就可以将这四种集合定一个 \([0, 3]\) 的数,然后考虑构造一个位运算。

容易得出 \(a \oplus b = \begin{cases}a & a=b\\3&a\ne b\end{cases}\)

需要满足 \(c_0, c_1, c_2 \in \{0, 1\},\ c_0 c_1 = c_0 c_2 = c_1 c_2 = c_3\),那么有 \([1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1]\) 四组解。

可以构造出 FWT 的转移矩阵 \(\begin{bmatrix}1 & 0 & 0 & 1\\0 & 1 & 0 & 1\\0 & 0 & 1 & 1\\0 & 0 & 0 & 1\end{bmatrix}\),其逆矩阵 \(\begin{bmatrix}1 & 0 & 0 & -1\\0 & 1 & 0 & -1\\0 & 0 & 1 & -1\\0 & 0 & 0 & 1\end{bmatrix}\)。实际上就是对于 \(3\) 这一位做了前缀和。

最后由于一位是对一个集合贡献,再类似于 FWT 转移一遍即可。

复杂度 \(O(k 4^k)\)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 18000005;
long long a[MAXN], b[MAXN], c[MAXN];
int k, n, x, y;
char s[123];
int mp[255];
void fwt(long long *t) {
    for (int mid = 1; mid < n; mid <<= 2) 
        for (int l = 0; l < n; l += (mid << 2)) 
            for (int i = 0; i < mid; i++) 
                t[l + i + 3 * mid] += t[l + i] + t[l + i + mid] + t[l + i + 2 * mid];
}
void ifwt(long long *t) {
    for (int mid = 1; mid < n; mid <<= 2) 
        for (int l = 0; l < n; l += (mid << 2)) 
            for (int i = 0; i < mid; i++) 
                t[l + i + 3 * mid] -= t[l + i] + t[l + i + mid] + t[l + i + 2 * mid];
}
int main() {
    scanf("%d%d%d", &k, &x, &y);
    n = 1 << (2 * k);
    mp['P'] = 0, mp['R'] = 1, mp['S'] = 2;
    for (int i = 1; i <= x; i++) {
        scanf("%s", s); int v = 0;
        for (int j = 0; j < k; j++) v |= mp[s[j]] << (2 * j);
        a[v] = 1;
    }
    for (int i = 1; i <= y; i++) {
        scanf("%s", s); int v = 0;
        for (int j = 0; j < k; j++) v |= mp[s[j]] << (2 * j);
        b[v] = 1;
    }
    fwt(a), fwt(b);
    for (int i = 0; i < n; i++) c[i] = a[i] * b[i];
    ifwt(c);
    for (int mid = 1; mid < n; mid <<= 2) 
        for (int l = 0; l < n; l += (mid << 2)) 
            for (int i = 0; i < mid; i++) {
                long long A = c[l + i], B = c[l + i + mid], C = c[l + i + 2 * mid], D = c[l + i + 3 * mid];
                c[l + i] = A + C + D;
                c[l + i + mid] = A + B + D;
                c[l + i + 2 * mid] = B + C + D;
                c[l + i + 3 * mid] = 0;
            }
    for (int i = 0; i < n; i++) {
        bool flag = true;
        int rev = 0;
        for (int j = 0; j < k; j++) {
            flag &= (i >> (2 * j) & 3) < 3;
            rev |= (i >> (2 * j) & 3) << ((k - j - 1) * 2);
        }
        if (flag) {
            c[rev] = 1ll * x * y - c[rev];
            printf("%lld\n", c[rev]);
        }
    }
    return 0;
}
posted @ 2023-03-16 16:22  APJifengc  阅读(47)  评论(0编辑  收藏  举报