【CF1463F】Max Correct Set

题目

题目链接:https://codeforces.com/contest/1463/problem/F
规定一个正整数集合 \(S\)。当且仅当满足以下条件时该集合成立:

  • \(S \subseteq \{1,2,...,n\}\)
  • 如果 \(a \in s\) 并且 \(b \in s\),那么 \(|a - b| \not ={x}\) 并且 \(|a - b| \not ={y}\)

对于给定的数值 \(n,x,y\),你需要找到成立集合的最大大小。
\(n\leq 10^9\)\(x,y\leq 22\)

思路

可以看作是一个长度为 \(n\)\(01\) 序列,要求任意的 \(1\) 的下标差不为 \(x\)\(y\)。最大化 \(1\) 的数量。
首先对于一个长度为 \(x+y\)\(01\) 序列,如果它满足上述条件,那么把它重复若干次都一定满足条件。否则把每 \(x+y\) 个看作一块,对于两个不同块的 \(1\),如果它们下标为 \(a,a+x\),那么必然 \(a+x-(x+y)=a-y\) 也是 \(1\),而 \(a\)\(a-y\) 必然在同一块,所以这块并不合法。
\(x<y\)\(O((x+y)2^y)\) 求出长度为 \(x+y\) 的块 \(1\) 最多出现次数。而答案就是这个块重复无限次的前 \(n\) 项。dp 的时候把贡献稍微改一下即可。

代码

#include <bits/stdc++.h>
using namespace std;

const int M=(1<<22);
int n,x,y,lim,ans,f[2][M];

int main()
{
	scanf("%d%d%d",&n,&x,&y);
	memset(f,0xcf,sizeof(f));
	f[0][0]=0; lim=(1<<max(x,y))-1;
	for (int i=0;i<x+y;i++)
	{
		int id=i&1,val=n/(x+y)+(i<n%(x+y));
		memset(f[id^1],0xcf,sizeof(f[id^1]));
		for (int s=0;s<=lim;s++)
		{
			int t=(s<<1)&lim;
			f[id^1][t]=max(f[id^1][t],f[id][s]);
			if (!(s&(1<<x-1)) && !(s&(1<<y-1)))
				f[id^1][t|1]=max(f[id^1][t|1],f[id][s]+val);
		}
	}
	int id=(x+y)&1;
	for (int s=0;s<=lim;s++)
		ans=max(ans,f[id][s]);
	cout<<ans;
	return 0;
}
posted @ 2021-09-11 17:08  stoorz  阅读(73)  评论(0编辑  收藏  举报