1. 程式人生 > 實用技巧 >NOIP 模擬賽DAY2

NOIP 模擬賽DAY2

NOIP 模擬賽 DAY2

2020.10.4 $\ $小雨 $\ \(上午\)\ $$8:00$~\(12:00\)

T1 按位或

考場上一直在想被\(3\)整除的數的二進位制數有什麼特點,倒是推出來了,但是還是解決不了此題,也想了想容斥,但還是被這個\(3\)的倍數卡住了思路。

到最後就沒有做此題的慾望了。(總是看了題解後才感慨:“哇,原來這麼簡單。”)

題解

首先不難想到暴力\(dp\),\(f[i][j]\)表示前\(i\)個數異或和為\(j\)的方案數,加上矩陣快速冪優化,期望得分\(40\)分。

正解需要拐一個彎,就是我們如果把給出的數字二進位制拆分,組成它的每一個二進位制數要麼\(mod\ 3\)

\(1\),要麼餘\(2\)

另外,考慮到幾個數按位或起來等於\(x\)的子集比等於\(x\)簡單的多,所以想到這裡可以容斥。

\(f[i][j]\)表示有\(i\)個餘\(1\)的二進位制項和\(j\)個餘\(2\)的二進位制項的子集數量(且每個子集都必須是3的倍數,用\(x+2*y\)來判斷)。

那麼答案就能容斥了。

#include <bits/stdc++.h>

typedef long long s64;

const int mod = 998244353;

inline void add(int &x, const int &y) {
    if (x += y, x >= mod)
        x -= mod;
}

inline void dec(int &x, const int &y) {
    if (x -= y, x < 0)
        x += mod;
}

inline int qpow(int x, s64 y) {
    int res = 1;
    for (; y; y >>= 1, x = 1LL * x * x % mod)
        if (y & 1)
            res = 1LL * res * x % mod;
    return res;
}

const int MaxN = 70;

s64 n, t;
int cnt1, cnt2;
int C[MaxN][MaxN], f[MaxN][MaxN];

int main() 
{
	
    std::cin >> n >> t;

    C[0][0] = 1;
    for (int i = 1; i <= 61; ++i) {
        C[i][0] = 1;
        for (int j = 1; j <= i; ++j) add(C[i][j] = C[i - 1][j - 1], C[i - 1][j]);
    }

    for (int i = 0; i <= 60; ++i)
        if (t >> i & 1)
            ++((1LL << i) % 3 == 1 ? cnt1 : cnt2);

    for (int i = 0; i <= cnt1; ++i)
        for (int j = 0; j <= cnt2; ++j)
            for (int a = 0; a <= i; ++a)
                for (int b = 0; b <= j; ++b)
                    if ((a + 2 * b) % 3 == 0)
                        add(f[i][j], 1LL * C[i][a] * C[j][b] % mod);

    int ans = 0;
    for (int i = 0; i <= cnt1; ++i)
        for (int j = 0; j <= cnt2; ++j)
            ((i + j) & 1 ? dec : add)(
                ans, 1LL * C[cnt1][i] * C[cnt2][j] % mod * qpow(f[cnt1 - i][cnt2 - j], n) % mod);
    std::cout << ans << '\n';

    return 0;
}