题目
题目链接:https://codeforces.com/contest/1463/problem/F
规定一个正整数集合 (S)。当且仅当满足以下条件时该集合成立:
- (S subseteq {1,2,...,n})
- 如果 (a in s) 并且 (b in s),那么 (|a - b| ot ={x}) 并且 (|a - b| ot ={y})
对于给定的数值 (n,x,y),你需要找到成立集合的最大大小。
(nleq 10^9),(x,yleq 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;
}