1. 程式人生 > 實用技巧 >CF449D Jzzhu and Numbers題解

CF449D Jzzhu and Numbers題解

link

CF449D Jzzhu and Numbers

solve

我們發現直接去統計方案數不大現實,我們就考慮用容斥的做法,用全部的方案數-不為 \(0\) 的方案數,我們就可以定義 \(F[i]\) 表示 \(a[i]\&i==i\) 的個數,我們先按位列舉,然後從1-1e6列舉j如果j在這一位上有數,我們就可以把這一位上的數去掉作為上一狀態轉移過來,即 \(dp[j^(1<<i-1)]+=dp[j]\) 為什麼要講小的狀態加上大的狀態,因為防止刷重

怎樣來確定正確性,我們先刷了去掉第一位的,然後列舉到第二位時刷去掉前兩位的和去掉第二位的,列舉到第n位時刷了前面的組合和去掉第n位的,所以我們把所有狀態都刷到了,因此答案是正確的。

考慮答案,我們發現,有些答案被統計了兩次,比如F[101]在F[100]的時候統計了一次,在F[001]的時候也被統計了一次,所以在我們要把F[101]減去一次,就是容斥的原理,所以二進位制下1的個數為奇數就加上,為偶數就減去,最後注意模數就好了。

code

程式碼的陣列名稱和題解不同

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define mod 1000000007
#define N 3000000
using namespace std;
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}
int n,x,dp[N],g[N];
long long f[N];
long long ans=0;
int main()
{
	scanf("%d",&n);
	int mx=(1<<20)-1;
	for(int i=1;i<=n;i++)dp[read()]++;
	f[0]=1;
	for(int i=1;i<=n;i++) f[i]=(f[i-1]<<1)%mod;
	for(int i=1;i<=20;i++)
	for(int j=0;j<=mx;j++)
	if(j&(1<<i-1)){
		dp[j^(1<<i-1)]+=dp[j];
	}
	ans=f[n]-1;
	for(int i=1;i <= mx;i++)
	{
		g[i]=g[i>>1]+(1&i);
		if(g[i]&1) ans=(ans-(f[dp[i]]-1)+mod)%mod;
		else ans=(ans+(f[dp[i]]-1)+mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}