Gym 102576B.Binomial
B.Binomial
先通过卢卡斯定理可推出\(C_{n}^{m}\)满足n&m=m时才为奇数。
卢卡斯定理\(C_{a}^{b}=C_{a/p}^{b/p}*C_{a\mod p}^{b \mod p}(\mod p)\)。由题意已知\(p=2\),那么就相当于把a,b按2进制拆位,要想最后的结果是奇数,那么对于每一个\(C_{a\mod p}^{b \mod p}\)这些情况下得出的结果必须是1,仅有\(C_{1}^{0},C_{1}^{1},C_{0}^{0}\)的情况下为1,那么推广开来就可以发现必须满足n&m=m这个条件。
然后就变成了SOS DP的板子题。
不进行状态优化的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 1e5 + 10;
int cnt[N], dp[N][21], a[N];
void solve() {
memset(cnt, 0, sizeof cnt);
memset(dp, 0, sizeof dp);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cnt[a[i]]++;
}
int m = (1 << 20);
for(int i = 1; i <= m; i++) {
dp[i][0] = cnt[i];
for(int j = 0; j < 20; j++) {
if(i & (1 << j)) {
dp[i][j + 1] = dp[i][j] + dp[i ^ (1 << j)][j];
}
else {
dp[i][j + 1] = dp[i][j];
}
}
}
LL res = 0;
for(int i = 1; i <= m; i++) {
if(cnt[i]) {
res += 1LL * cnt[i] * dp[i][20];
}
}
printf("%lld\n", res);
}
int main() {
// freopen("in.txt", "r", stdin);
int t; cin >> t; while(t--)
solve();
return 0;
}
优化代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
int cnt[N], dp[N], a[N];
void solve() {
memset(cnt, 0, sizeof cnt);
memset(dp, 0, sizeof dp);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cnt[a[i]]++;
dp[a[i]]++;
}
int m = (1 << 20);
for(int i = 0; i <= 20; i++) {
for(int j = m; j >= 0; j--) {
if(j & (1 << i)) dp[j] += dp[j ^ (1 << i)];
}
}
LL res = 0;
for(int i = 1; i <= m; i++) {
if(cnt[i]) {
res += 1LL * cnt[i] * dp[i];
}
}
printf("%lld\n", res);
}
int main() {
// freopen("in.txt", "r", stdin);
int t; cin >> t; while(t--)
solve();
return 0;
}