题目大意:
你在某社交网站上面注册了一个新账号,这个账号有 n ( n ≤ 1 0 5 n(n\leq 10^5 n(n≤105
)次记录。要么就是你更改过一次 I D ID ID,要么就是一个 I D ID ID为 s ( ∣ s ∣ ≤ 40 ) s(|s|\leq 40) s(∣s∣≤40)的朋友访问过你的空间。
你有 m ( m ≤ 40 ) m(m\leq 40) m(m≤40)个朋友。每一个朋友都会访问你的空间至少一次。如果这一个朋友每一次访问你的空间的时候,你的 I D ID ID和它的 I D ID ID一样,那么他就会高兴。 求你最多能让多少人高兴。
题目思路:
首先考虑选择的最终状态
那么必然是任意两个相邻 1 1 1之间的所有访客记录都是互斥的。
换句话说,这些访客记录里只能有一个开心的访客
所以最后选出的访客集合可以这么解释:不可能有两个及以上的在相邻1操作间
的访客存在于这个集合中
考虑转换为图论,将互斥的关系建边,所以这是显然的最大独立集了,考虑二分图?当然不是,因为并不能保证这是一个二分图。
看到 m m m的范围 m ≤ 40 m \le40 m≤40,所以可以考虑折半 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] MAXS⊕b[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
***/