CF1105E Helping Hiasat

cf

luogu

先将问题转换.由于一个网友要一直和他同名答案才能+1,所以对于一个改名的间隔,如果要选这个网友就不能选其他网友,所以对于两个1操作之间的所有网友分别相互连边.最后我们得到了一张图,现在问题是无向图最大独立集

\(n\le 40\),那就\(meet\ in\ the\ middle\),点集分为两半,然后分别暴力枚举集合,再枚举左边的某个集合,右边的集合能选当且仅当不存在和右边集合有连边的点,所以可以预处理出\(g_i\)表示右边超集为\(i\)的独立集大小最大值,可以高维前缀和实现.再预处理\(h_i\)表示左边独立集\(i\)在右边点的连边情况,同样可以高维前缀和.然后每次查一下就好了

upd:上面两个信息可以不用高维前缀和,因为子集max计算是可以算重复贡献的,所以一个\(i\)可以由\(i-lowbit(i)\)\(i-hignbit(i)\)转移过来

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=(1<<20)+10,M=45;
int rd()
{
	int x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
map<string,int> id;
bool v[M],mp[M][M];
int q,n,nn,stk[M],tp,zt[N],f[N],g[N],l2[N];
char cc[M];

int main()
{
    //////////////
	q=rd(),n=rd();
	while(q--)
	{
		int op=rd();
		if(op==1)
		{
			for(int i=1;i<=tp;++i)
				for(int j=i+1;j<=tp;++j)
					mp[stk[i]][stk[j]]=mp[stk[j]][stk[i]]=1;
			while(tp) v[stk[tp]]=0,--tp;
		}
		else
		{
			scanf("%s",cc);
			if(!id[cc]) id[cc]=++nn;
			if(!v[id[cc]]) v[id[cc]]=1,stk[++tp]=id[cc];
		}
	}
	for(int i=1;i<=tp;++i)
		for(int j=i+1;j<=tp;++j)
			mp[stk[i]][stk[j]]=mp[stk[j]][stk[i]]=1;
	while(tp) v[stk[tp]]=0,--tp;
	for(int i=1;i<=n/2;++i)
	{
		for(int j=1;j<=n-n/2;++j)
			zt[1<<(i-1)]|=mp[i][n/2+j]<<(j-1);
	}
	for(int i=1;i<=1<<20;++i) l2[i]=l2[i>>1]+1;
	memset(f,-0x3f3f3f,sizeof(f));
	f[0]=0;
	for(int i=1;i<1<<(n/2);++i)
	{
		int j=i^(i&(-i));
		bool ok=1;
		for(int k=1;ok&&k<=n/2;++k)
			ok=!(i>>(k-1)&1)||!mp[l2[i^j]][k];
		if(ok) f[i]=f[j]+1;
	}
	memset(g,-0x3f3f3f,sizeof(g));
	g[0]=0;
	for(int i=1;i<1<<(n-n/2);++i)
	{
		int j=i^(i&(-i));
		bool ok=1;
		for(int k=1;ok&&k<=n-n/2;++k)
			ok=!(i>>(k-1)&1)||!mp[l2[i^j]+n/2][k+n/2];
		if(ok) g[i]=g[j]+1;
	}
	for(int j=1;j<1<<(n/2);j<<=1)
		for(int i=0;i<1<<(n/2);++i)
			if((i&j)==j) zt[i]|=zt[i^j];
	for(int j=1;j<1<<(n-n/2);j<<=1)
		for(int i=0;i<1<<(n-n/2);++i)
			if((i&j)==j) g[i]=max(g[i],g[i^j]);
	int ans=0,u=(1<<(n-n/2))-1;
	for(int i=0;i<1<<(n/2);++i)
		ans=max(ans,f[i]+g[u^zt[i]]);
	printf("%d\n",ans);
    return 0;
}
posted @ 2019-09-26 22:36  ✡smy✡  阅读(184)  评论(3编辑  收藏  举报