Deltix Round, Spring 2021 (open for everyone, rated, Div. 1 + Div. 2) D. Love-Hate

Deltix Round, Spring 2021 (open for everyone, rated, Div. 1 + Div. 2) D. Love-Hate

题目链接:https://codeforces.ml/contest/1523/problem/D

题意:

n (1≤n≤2*10^5)个朋友来聚会,总共有m (1≤p≤m≤60)个话题,每个朋友最多喜欢p (p≤15)个话题,你需要选择一些话题,使得喜欢所有你选择话题的人数大于等于总人数的一半。求最多可以选择多少个话题,输出一种方案。

题解:

一种随机的思路。

最后得到的答案一定是某一个方案的子集,那么枚举每一种方案去和其他的方案求交集似乎可行。

如何求解交集?

状态压缩,对p进行状压,将状态设计成是否选择模板中的每个1。

问题变为找出一个可行的方案,他被覆盖到的次数超过(n + 1)/2,即给定一个状态,如何快速枚举其子集并统计其出现的个数

状态S和他所有父集被覆盖的次数就是方案可以覆盖的个数,对于每个状态状压枚举其子集,然后维护一个cnt 数组计数

所以考虑用状压 dp 去维护前缀和的思想,假设现在有两个状态 i , j,满足 i是 j的一个子集,即 i & j = j,则根据前缀和的思想,可以有 cnt[j] += cnt[i]

所以以O(n * p)的复杂度处理初始的cnt数组,然后O(p * 2 ^ p)状压dp维护一下前缀和

解题代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) 
const int INF = 0x3f3f3f3f;
const int N = 1e6+100;
mt19937_64 eng(time(NULL));
LL s[N];
int cnt[(1<<15)+100];
int a[N];
int main(){
	int n,m,p;
	cin >> n >> m >> p;
	for(int i = 1;i <= n;i++) {
		string str;
		cin >> str;
		for(int j = 0;j < m;j++) {
			if(str[j] == '1') {
				s[i] |= (1LL << j);//将要求转为数字存储,方便异或计算 
			}
		}
	}
	iota(a + 1,a + 1 + n, 1);//将a数组赋值为 1,2,3,4,5... 
	shuffle(a + 1,a + 1 + n,eng);//打乱顺序 
	
	int ans = -1;
	string res;
	for(int t = 1;t <= min(50,n);t++) {
		memset(cnt,0,sizeof(cnt));//每回枚举重置 
		int p = a[t];//获取随机后的位置 
		vector<int>bits;//存储1的位置 
		for(int j=0;j<m;j++) {//当前随机到的数据里哪里有1 
			if((s[p] >> j) & 1) {
				bits.push_back(j);
			}
		}
		int sz = bits.size();
		for(int i = 1;i <= n;i++) { //n*p
			int state = 0;//取交集后的状态 
			for(int j=0;j<sz;j++) {
				if((s[i] >> bits[j]) & 1) {
					state |= (1<<j);
				}
			}
			cnt[state]++;//这里可以知道对于当前的状态与后可一提供多少个 
		}
		
		//将子并到父中去 
		for(int j=0;j<sz;j++) {//p*2^p
			for(int i = 0;i < 1<<sz;i++) {
				if(((i>>j)&1)==0) {
					cnt[i] += cnt[i|(1<<j)];
				}
			}
		}
		
		//__builtin_popcount 计算二进制中有几个1 
		//枚举当前随机方案的子集 
		for(int i = 0;i< 1 << sz;i++) {
			//如果当前方案满足个数而且其中的1更多即选的更多 
			if(cnt[i] >= (n+1)/2 && __builtin_popcount(i) > ans) {
				ans = __builtin_popcount(i);//更新新的方案 
				res = string(m,'0');//将res初始化,长度为m个0 
				//将res赋值为当前状态 
				for(int j = 0;j < sz;j++) {
					if((i>>j)&1) {
						res[bits[j]]='1';
					}
				}
			}
		}
	}
	cout << res << endl;
	return 0;
}
posted @ 2021-06-15 16:04  0xYuk1  阅读(97)  评论(0)    收藏  举报