反思
\(\qquad\) 赛时只过了一题,被两道定理题卡住了,一道不知道定理,一道代码实现不出来。结果赛后发现另一道模拟其实能做。以后还是得把题都看一遍,不能只看榜单qwq。
1001 数列计数
数论+位运算
/*思路:我们需要让累乘之后的数字为奇数,那么对于每一个 C(ai, bi) 都只能为奇数。因此我们只需要找到 li 范围内有多少个可以使 C(ai, bi) 为奇数的数然后累乘取模即可。那我们如何判断一个组合数的奇偶性呢?就要引出 lucas 定理了。lucas 定理:对于素数 p 存在:C(n, k) % p == C(n/p, k/p)*C(n%p, k%p) % p;而对于判断奇偶性,显然 p = 2 ,此时 lucas 定理便可以变形为:C(n, k) % 2 == C(n/2, k/2)*C(n%2, k%2) % 2;通过观察,我们可以发现,对于一个组合数 C(n, k) % 2 其实就是对其进行二进制拆分,并且累乘的结果,即我们假设 n, k 最高位为第 m 位,那么就可以简单表示为:C(n, k) % 2 == C(n0, k0)*...*C(ni, ki)*...*C(nm, km); (0 <= i <= m);然后我们发现,对于 C(n, k) % 2 二进制拆分后的每一位,我们有四种情况:C(1, 1), C(1, 0), C(0, 1), C(0, 0);而其中只有当 C(0, 1) 时结果为 0;所以我们得出结论,对于组合数 C(n, k) % 2 == 1 我们需要使 k 在二进制表示下的 0, 1 数列被 n 严格包含。即:只有 n 的第 i 位为 1 时,k 的第 i 位才能考虑为 1 的情况。在明确 ai | bi == ai 这一点后,我们就要开始考虑限制条件 li 了。对此,我们可以对 ai 与 li 同时从最高位开始提取,就会出现四种情况,下面对这四种情况进行分类讨论:我们定义 ak, lk 分别为 ai, li 当前提取出的二进制数字。1) lk == 0 时,我们发现 ak 上无论是 1 还是 0 都不会对计数产生贡献。2) lk == 1, ak == 1 时,我们简单地把 ak 位上的数字当作 0, 此时 0~k-1 位上的 1 不论如何都能对计数产生贡献。3) 1k == 1, ak == 0 时,此时,我们便可以隐式判断当情况 2) 出现时 ak 取 1 的计数:我们假设 1101(13) 和 1011(11) 在比较第 3 位时,我们发现 a3 = l3 = 1 ,由 2) 的判断,我们将 0101,0001,0100,0000 加入计数。而比较到第 2 位时,因为 l2 = 0,因此跳过判断。比较到第 1 位时,我们发现 l1 = 1, 而 a1 = 0, 此时,我们发现在第 0 位上的数字不论取何值,加上第 3 位上的数字都不会超过 li,因此我们将 1001, 1000 加入计数,同时退出计数。PS:在遍历过程中,即使没有遇到 3) 的情况,也会由 2) 以同样的原理隐式判断上一次的 2)。4) 当遍历到第 0 位的二进制比较都无法满足 3) 时,我们发现遍历过程中最近的一次 2) 并没有被隐式判断。也就是说可能会出现 (a) 101, (l) 100的情况。我们便要对计数 +1 来将类似 100 的情况加入计数。*/#include <bits/stdc++.h>using namespace std;#define int long long
using ll = long long;
using ull = unsigned long long;
using uint = unsigned;
using pii = pair<int, int>;
const int MOD = 998244353;int calc(int x, int y){int res = 0;int cnt = __builtin_popcount(x); //计算 x 在二进制表示下有多少个 1for(int i = 30; i >= 0; i -- ){ //从最高位开始提取int tx = (x >> i) & 1, ty = (y >> i) & 1; //提取 x, y 当前位的数。if(tx) cnt--; //如果 x 当前位为 1 则说明当前位置往前还剩下 cnt-1 个 1if(!ty) continue; //如果 y 当前位为 0 则跳过if(tx) res += (1 << cnt); //如果 y 与 x 的当前位都是 1 则对往前位置的每一个 1 计算贡献加入计数if(!tx) return res + (1 << cnt); //如果 y 的当前位为 1,但 x 的当前位为 0 则将当前位的所有 1 计算贡献并退出程序}return res + 1; //加上最后一个没有被隐式判断的 1
}void solve(){int n;cin >> n;vector<int> a(n+1), l(n+1);for(int i = 1; i <= n; i ++ ) cin >> a[i];for(int i = 1; i <= n; i ++ ) cin >> l[i];int ans = 1;for(int i = 1; i <= n; i ++ ){ans = 1LL * ans * calc(a[i], l[i]) % MOD; //累乘并取模}cout << ans << endl;
}signed main(){ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);int t = 1;cin >> t;while(t--){solve();}return 0;
}