CF1463F Max Correct Set
题意
现求一个集合 \(S\subseteq\{1,2,\cdots,n\}\),要求满足任意两个在此集合内的数 \(a,b\),它们的差 \(|a-b|\not ={x}\) 并且 \(|a-b|\not ={y}\)。求集合 \(S\) 的最大大小。
Solution
感觉 \(n\) 很大,\(x,y\) 很小,总感觉就是状压。
界外,首先可以想到暴力的状压,就是你考虑当前数能不能选只和前面的 \(\max(x,y)\) 个数有关,所以状压一下前面 \(\max(x,y)\) 个数就可以了。但是这个东西是 \(O(2^{\max(x,y)}n)\) 的,非常 GG 啊。
\(\color{red}{\bigstar}\) 如果没什么思路可以从特殊情况考虑起,就是如果有 \(x=y\) 成立,这里可以怎么方便地来做。
容易想到,直接按照长度为 \(x\) 分成若干段,奇数段全部是 \(1\),偶数段全部是 \(0\)。这样肯定是最大的。然后来考虑这其实以 \(2x\) 为循环节来构造一组最优解。大胆猜测,当 \(x\not= y\) 时,有同样的结论,即存在一个以 \(x+y\) 为循环节的最优解。
这样的话,我们暴力做前 \(x+y\) 个的结果,然后把这一区间的最优结果乘上最终能贡献几段来 dp。复杂度是 \(O(2^{\max(x+y)}(x+y))\)。
这样构造凭什么一定合法?我们已经保证了每一块内是合法的,也就是我们只担心两块间有两个位置不合法,我们假设是 \(j-i=x\),那么会有 \(i-j+(x+y)=y\),这样子 \(x\) 和 \(y\) 肯定不会同时为 \(1\)。那这样为什么是最优呢?如果在固定了初始的 \(x+y\) 个位置之后,如果后面出现了更优的方式,直接替换掉肯定会变优。(但是这只是粗略的想法,具体证法狗都不证)
Code
// Problem: CF1463F Max Correct Set
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1463F
// Memory Limit: 3100 MB
// Time Limit: 250000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
int dp[2][(1<<22)+5];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,x,y,m,M;cin>>n>>x>>y;m=x+y;M=max(x,y);
int op=1,lmt=(1<<M)-1;
rep(i,1,m){
op^=1;
memset(dp[op],0,sizeof(dp[op]));
rep(j,0,lmt){
dp[op][(j<<1)&lmt]=max(dp[op][(j<<1)&lmt],dp[op^1][j]);
if(!((j>>(x-1))&1)&&!((j>>(y-1))&1))
dp[op][(j<<1|1)&lmt]=max(dp[op][(j<<1|1)&lmt],
dp[op^1][j]+n/m+(n%m>=i));
}
}
int ans=0;
rep(i,0,(1<<M)-1) ans=max(ans,dp[op][i]);
cout<<ans<<'\n';
return 0;
}