NOIP 模擬賽DAY2
阿新 • • 發佈:2020-10-05
NOIP 模擬賽 DAY2
2020.10.4 $\ $小雨 $\ \(上午\)\ $$8:00$~\(12:00\)
T1 按位或
考場上一直在想被\(3\)整除的數的二進位制數有什麼特點,倒是推出來了,但是還是解決不了此題,也想了想容斥,但還是被這個\(3\)的倍數卡住了思路。
到最後就沒有做此題的慾望了。(總是看了題解後才感慨:“哇,原來這麼簡單。”)
題解
首先不難想到暴力\(dp\),\(f[i][j]\)表示前\(i\)個數異或和為\(j\)的方案數,加上矩陣快速冪優化,期望得分\(40\)分。
正解需要拐一個彎,就是我們如果把給出的數字二進位制拆分,組成它的每一個二進位制數要麼\(mod\ 3\)
另外,考慮到幾個數按位或起來等於\(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; }