Deltix Round, Spring 2021 (open for everyone, rated, Div. 1 + Div. 2) D. Love-Hate
阿新 • • 發佈:2021-06-15
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; }