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;
}
posted @ 2021-03-27 09:26  这知识他不进我的脑子  阅读(67)  评论(0编辑  收藏  举报