P5387 [Cnoi2019]人形演舞

inline int qmod(int& x) {
    return x < mod ? x : x -= mod;
}
template<const int mod = 998244353>
struct FWT{
    const int inv2  = ksm(2, mod - 2);
    int l;
    void init(int n) {
        l = 2;
        while(l < n)    l <<= 1;//l <= n?
    }
    inline int qmod(int x) {
        while(x >= mod) x -= mod;
        return x;
    }
    void fwt(vector<int> &a, int opt, int cas) {
        a.resize(l);//l + 1?
        int x, y;
        for(int k = 2; k <= l; k <<= 1) {
            int mid = k >> 1;
            for(int i = 0; i < l; i += k) {
                for(int j = i, up = i + mid; j < up; ++ j) {
                    x = a[j], y = a[j + mid];
                    a[j] = qmod(x + y);
                    a[j + mid] = qmod(x - y + mod);
                    if(opt == -1) a[j] = a[j] * inv2 % mod, a[j + mid] = a[j + mid] * inv2 % mod;
                }
            }
        }
    }
};
FWT<> f;
//转二进制后,每个1的位置对应的数[2^k, 2^{k + 1} - 1]都能取到,还有就是自己一定取不到
//对于每个数可以拿的次数是1、只有1个1 -> 1.  2、多个1找第二大的1,次数就是这个1加上1
//显然就可以把每堆变为他可以拿的次数,显然每堆的次数异或为0算1个贡献,求出的次数作为下标的值通过fwt求即可
//必须取mx,不然复杂度不对的
signed main() {
    int n = rd(), m = rd(), mx = 0;//n个数的集合,每个数范围[1, m]
    vector<int> a(m + 1);
    for(int i = 1, now = 1; i <= m; ++ i) {
        if(i > now && i % now == 0)  now <<= 1;
        a[i - now + 1] ++;  mx = max(mx, i - now + 1);
    }
    //nim博弈,每堆异或不为0必胜!
    f.init(mx + 1);
    f.fwt(a, 1, 3); n %= (mod - 1);
    for(int i = 0; i < f.l; ++ i)   a[i] = ksm(a[i], n);
    f.fwt(a, -1, 3);
    int ans = 0;
    for(int i = 1; i < f.l; ++ i)  ans += a[i];
    printf("%lld\n", ans % mod);
    return 0;
}

posted @ 2021-08-04 18:57  wlhp  阅读(69)  评论(0)    收藏  举报