【题解】Codeforces1523D Love-Hate

Codeforces1523D Love-Hate

题意

(n)个集合与和一个有(m)个元素的全集(U),每个集合都是(U)的子集,且每个集合大小不超过(p),求一个最大的(U)的子集(A),使得(A)至少是(n)个集合中(lceil frac{n}{2} ceil)个集合的子集。

(1le nle 2 imes10^5,1le ple mle 60,1le ple 15)

题解

如果我么随机从(n)个和中随机选(k)个集合,再枚举它的子集作为答案。由于(A)至少是(n)个集合中(lceil frac{n}{2} ceil)个集合的子集,这样错误的概率为(frac{1}{2^k}),错误的概率很小,所以我们随机一定次数从(n)个集合中选集合然后验证。下面考虑如何快速验证一个集合是否满足条件。

由于每个集合最多只有(p)个元素,我们只需考虑其他集合中这(p)个元素的存在情况。于是可以根据当前待验证的集合将其他集合表示为长度不超过(p)的位图,设他们分别为(a_i),不妨设当前集合为(a_1),则其一个子集(b(b&a_1=b,b|a_1=a_1))能作为答案的条件为满足(b&a_i=b)(a_i)不少于(lceil frac{n}{2} ceil)个。于是可以用(FWT)来统计答案,令(A_k=sum_{i=2}^{n}[a_i==k]),对于(fwtand)

[FWTAND[A]_i=sum_{j&i=i}A_j ]

故此情况下可以将答案更新为(max{ card(i) |A_igelceil frac{n}{2} ceil})

(T)为随机的次数,则总复杂度为(O(Tp(2^p+n)))

(old{PS:})生成的随机数类型为unsigned int,范围为(0sim 2^{32}-1),如果要得到的随机数为int要先模(n)再强转为int,否则会出现负数!

#include <bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N=3e5+10,INF=1<<30;
int n,m,p=0,c[N],aa[N],res[62];
char s[75]; 
int pc[(1<<15)+10];
void fwtand(ll *a,int n,int typ){
	for(int mid=1;mid<n;mid<<=1){
		for(int r=(mid<<1),j=0;j<n;j+=r){
			for(int i=0;i<mid;i++){
				a[i+j]=(a[i+j]+typ*a[i+j+mid]);
			}
		}
	}
}
ll ov[N];
ll a[(1<<15)+10];
unsigned sd=chrono::system_clock::now().time_since_epoch().count();
mt19937 rndnum(sd);
int rnd(int n){
	unsigned res=rndnum()%n+1;
	return res;
}
int vis[N];
void f1(){
	for(int i=1,l=(1<<15);i<l;i++)pc[i]=pc[i>>1]+(i&1);
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++){
		//aa[i]=i;
		vis[i]=0;
		scanf("%s",s+1);
		for(int j=1;j<=m;j++){
			if(s[j]=='1'){ov[i]|=(1ll<<(j-1));}
		}
	}
	int ans=0;
	for(int o=1;o<=min(200,n);o++){
		int u=rnd(n);
		while(vis[u]){
			u=rnd(n);
			//assert(u<=n&&u>=1);
		}
		vis[u]=1;
		ll na=ov[u];p=0;
		for(int i=1;i<=m;i++){
			if((1ll<<(i-1))&na){c[++p]=i;}
		}
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++){
			ll y=(na&ov[i]),z=0;
			for(int j=1;j<=p;j++){
				if((1ll<<(c[j]-1))&y){
					z|=(1<<(j-1));
				}
			}
			++a[z];
		}
		int l=(1<<p);
		fwtand(a,l,1);
		for(int i=0;i<l;i++)if(a[i]*2>=n&&pc[i]>ans){
			ans=pc[i];
			memset(res,0,sizeof(res));
			for(int j=1;j<=p;j++)if(i&(1<<(j-1))){
				res[c[j]]=1;
			}	
		}
	}
	for(int i=1;i<=m;i++){printf("%d",res[i]);}
}
int main(){
	//int t;
	//scanf("%d",&t);
	//while(t--)
		f1();
	return 0;
}
/*
3 4 3
1000
0110
1001

*/
原文地址:https://www.cnblogs.com/bobh/p/15031782.html