【Codeforces 1105E】Helping Hiasat | 状态压缩、最大独立集、折半

题目大意:

你在某社交网站上面注册了一个新账号,这个账号有 n ( n ≤ 1 0 5 n(n\leq 10^5 n(n105
)次记录。要么就是你更改过一次 I D ID ID,要么就是一个 I D ID ID s ( ∣ s ∣ ≤ 40 ) s(|s|\leq 40) s(s40)的朋友访问过你的空间。

你有 m ( m ≤ 40 ) m(m\leq 40) m(m40)个朋友。每一个朋友都会访问你的空间至少一次。如果这一个朋友每一次访问你的空间的时候,你的 I D ID ID和它的 I D ID ID一样,那么他就会高兴。 求你最多能让多少人高兴。

题目思路:

首先考虑选择的最终状态
那么必然是任意两个相邻 1 1 1之间的所有访客记录都是互斥的。

换句话说,这些访客记录里只能有一个开心的访客

所以最后选出的访客集合可以这么解释:不可能有两个及以上的在相邻1操作间的访客存在于这个集合中

考虑转换为图论,将互斥的关系建边,所以这是显然的最大独立集了,考虑二分图?当然不是,因为并不能保证这是一个二分图。

看到 m m m的范围 m ≤ 40 m \le40 m40,所以可以考虑折半 m m m

进行状压dp,将集合分成两半,考虑前一半集合的独立集情况,后一半集合的独立集情况,最后考虑两个集合合并时的独立集情况。假设 s s s t t t合并,那么必然有 s s s互斥的点集 在 t t t中不存在,所以能够与前一半 s s s集合的组合的后一半集合一定满足是 M A X S ⊕ b [ s ] MAXS⊕b[s] MAXSb[s]的子集。所以这样只需要状压 d p dp dp预处理一下 s s s所有子集的最大值就好了

( m / 2 ) ∗ 2 m / 2 (m/2)*2^{m/2} (m/2)2m/2去处理互斥点集和状压dp

所以总的复杂度: O ( ( m / 2 ) ∗ 2 m / 2 ) O((m/2)*2^{m/2}) O((m/2)2m/2)

Code:

/*** keep hungry and calm CoolGuang!  ***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17+7;
const ll maxn = 2e6+700;
const ll mod= 1e9+7;
const ll up = 1e13;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){
    
    char c=getchar();T x=0,f=1;while(!isdigit(c)){
    
    if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){
    
    x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
vector<int>v;
map<string,int>mp;
int t[55][55];
int vis[55];
int b[maxn],dp[maxn];
int isd[maxn],c[maxn],s[maxn];
int S[maxn],T[maxn];
int work(){
    
    
	int ans = 0;
	int half = m/2;
	int MAX  = 1<<half;
	for(int i=1;i<=half;i++){
    
    
		for(int k=half+1;k<=m;k++){
    
    
			if(t[i][k]) b[i] |= 1<<(k-half-1);
		}
	}
	for(int i=1;i<=half;i++)
		for(int k=1;k<=half;k++)
			if(t[i][k]) S[i] |= 1<<(k-1);
	for(int i=half+1;i<=m;i++)
		for(int k=half+1;k<=m;k++)
			if(t[i][k]) T[i] |= 1<<(k-half-1);

	for(int i=0;i<MAX;i++){
    
    
		int flag = 1;
		for(int k=1;k<=half;k++){
    
    
			if(i>>(k-1)&1){
    
    
				c[i]++;
				s[i] |= b[k];
			}
			if((i>>(k-1)&1)&&(S[k]&i)) flag = 0;
		}
		isd[i] = flag;
		if(isd[i]) ans = max(ans,c[i]);
	}


	ll thalf = m - half;
	MAX = 1<<thalf;
	for(int i=0;i<MAX;i++){
    
    
		int flag = 1,cot = 0;
		for(int k=half+1;k<=m;k++){
    
    
			if(i>>(k-half-1)&1) cot++;
			if((i>>(k-half-1)&1) && (T[k]&i)) flag = 0;
		}
		if(flag) dp[i] = cot,ans = max(ans,cot);
	}

	for(int i=0;i<MAX;i++){
    
    
		for(int k=1;k<=thalf;k++)
			if(i>>(k-1)&1) dp[i] = max(dp[i],dp[i^(1<<(k-1))]);
	}

	MAX = 1<<half;
	int MAXS = (1<<thalf)-1;
	for(int i=0;i<MAX;i++){
    
    
		if(isd[i]) ans = max(ans,c[i]+dp[MAXS^s[i]]);
	}
	return ans;
}
int main(){
    
    
	read(n);read(m);
	int cnt = 0;
	for(int i=1;i<=n;i++){
    
    
		int op;read(op);
		if(op == 1)
			for(int k=1;k<=m;k++) vis[k] = 0;
		else{
    
    
			string s;cin>>s;
			if(!mp[s]) mp[s] = ++cnt;
			int aim = mp[s];
			for(int k=1;k<=m;k++)
				if(vis[k] && k!=aim) t[aim][k] = t[k][aim] = 1;
			vis[aim] = 1;
		}
	}
	di(work());
	return 0;
}
/***
6 3
1
2 aa
2 bb
1
2 aa
2 cc
***/

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/112450184
今日推荐