[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;
}
posted @ 2025-04-14 21:04  JoeyJiang  阅读(12)  评论(0)    收藏  举报