P3622 [APIO2007]动物园

题目

P3622 [APIO2007]动物园

给定一个有\(n\)个动物的环,你可以移除某些动物,有\(c\)个小朋友,每个小朋友能看到\(5\)个动物,每个小朋友有喜欢的和不喜欢的动物

定义小朋友高兴为

  • 至少看到他喜欢的一个动物
  • 至少有一个他不喜欢的动物被移除

问最多有多少小朋友高兴

\(10\le n \le10^4,1\le c\le5*10^5\)

思路

每个人只能看到\(5\)个动物而且每种动物只有\(0/1\)两种状态,这提示我们使用状压来做

不妨先考虑环的问题,显然可以每\(5\)个划分状态

\(f[i][s]\)表示从到\(i\)\([i,i+5)\)这几个动物的状态为\(s\)时最多能使多少小朋友开心

\(f[i][s]=\max(f[i-1][(s\&15)<<1],f[i-1][(s\&15)<<1|1])+sum[i][s]\)

\(sum[i][s]\)表示状态是\(s\),视野为\([i,i+5)\)的小朋友高兴的人数

  • 对环的处理

环上第\(n\)个动物时的状态\(s\)的后四位必须与第\(1\)个动物的前四位的状态相同

则只需强制开始状态和结束状态一样才能更新

code

/*
@ author:pyyyyyy/guhl37
-----思路------

-----debug-------

*/
#include<bits/stdc++.h>
using namespace std;
int n,c,ans,E,F,L;
int num[50005][35],f[10005][35]; 
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>n>>c;
	for(int i=1;i<=c;++i)
	{
		cin>>E>>F>>L;
		int x,dislike=0,like=0;
		for(int j=1;j<=F;++j)
		{
			scanf("%d",&x);
			x=(x-E+n)%n;
			like|=(1<<x);
		}
		for(int j=1;j<=L;++j)
		{
			scanf("%d",&x);
			x=(x-E+n)%n;
			dislike|=(1<<x);
		}
		for(int j=0;j<32;++j)
			if((dislike&~j)||(like&j))
				num[E][j]++;
	}
	for(int i=0;i<32;++i)
	{
		memset(f[0],128,sizeof(f[0]));
		f[0][i]=0;
		for(int j=1;j<=n;++j)
			for(int s=0;s<32;++s)
				f[j][s]=max(f[j-1][(s&15)<<1],f[j-1][(s&15)<<1|1])+num[j][s];			
		if(ans<f[n][i]) ans=f[n][i];
	}
	cout<<ans;
	return 0;
}
posted @ 2020-06-17 21:59  pyyyyyy  阅读(105)  评论(0编辑  收藏  举报