[ARC106] E - Medals
题面
题目描述
有 \(n\) 名员工,员工 \(i\) 上班的周期形如第 \((0,a_i]\) 天上班,第 \((a_i,2a_i]\) 天不上,第 \((2a_i,3a_i]\) 天上班,第 \((3a_i,4a_i]\) 天不上……以此类推。现在每天可以选定一个上班的员工颁发一个奖牌,问至少需要几天可以使每位员工拥有至少 \(k\) 个奖牌。
数据范围
-
\(1\leq n\leq 18\)。
-
\(1\leq a_i,k\leq 10^5\)。
题解
首先答案有上界 \(2nk\),一种显然的颁发方式是一个一个人颁发,每个人至多需要 \(2k\) 天颁发完。
考虑二分答案,则问题变为是否可以在 \(m\) 天颁发完并达成目标。考虑 Hall 定理,题目中的一个上班的员工颁发一个奖牌可以变成匹配的形式,则可以通过枚举员工的子集判断是否满足 Hall 定理的题设,即每个左部点子集的领域大小大于子集本身的大小。
现在的问题是如何快速找出若干员工上班日的并集。正难则反,考虑求员工不上班日的交集,令某一天不上班的员工集合为 \(S\),则这一天会对 \(S\) 的所有子集 \(T\) 造成 \(1\) 的贡献,这使人想到高维后缀和/SOS DP/And 卷积。具体的,处理出每一天的不上班员工集合,并在二分时把所有 \(\leq m\) 的日子按 \(S\) 装进桶里,做高维后缀和。
于是有时间复杂度 \(O(n^2k+n2^n\log nk)\)。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N=2e6+9;
int a[N],bel[N],c[N],lim,n,k;
inline bool Check(int m){
for(int i=0;i<(1<<n);i++) c[i]=0;
for(int i=1;i<=m;i++) c[bel[i]]++;
for(int k=0;k<n;k++) for(int i=0;i<(1<<n);i++) if(~i>>k&1) c[i]+=c[i|(1<<k)];
for(int i=0;i<(1<<n);i++) if(k*__builtin_popcount(i)>m-c[i]) return 0;
return 1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
lim=2*n*k;
for(int i=1;i<=lim;i++) for(int j=1;j<=n;j++) if((i-1)/a[j]&1) bel[i]|=1<<j-1;
int l=0,r=lim;
while(l+1<r){
int mid=l+r>>1;
if(Check(mid)) r=mid;
else l=mid;
}
cout<<r<<endl;
return 0;
}

浙公网安备 33010602011771号