欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

ARC106E-Medals【hall定理,高维前缀和】

发布时间:2023/12/3 编程问答 52 豆豆
生活随笔 收集整理的这篇文章主要介绍了 ARC106E-Medals【hall定理,高维前缀和】 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

正题

题目链接:https://atcoder.jp/contests/arc106/tasks/arc106_e


题目大意

nnn个员工,第iii个在[1,Ai][1,A_i][1,Ai]工作,[Ai+1,2×Ai][A_i+1,2\times A_{i}][Ai+1,2×Ai]休息,[2×Ai+1,3×Ai][2\times A_i+1,3\times A_i][2×Ai+1,3×Ai]工作…以此类推。

然后每天可以为一个在工作的人发一枚奖牌,至少多少天才能让每个人都有kkk块奖牌。

1≤n≤18,1≤k,Ai≤1051\leq n\leq 18,1\leq k,A_i\leq 10^51n18,1k,Ai105


解题思路

首先答案上限显然是2×n×1052\times n\times 10^52×n×105(就是每个人轮流发)

然后考虑一个暴力的做法。先二分,然后把每天看做一个右边的点,每个员工看做kkk个左边的点,然后一个员工干活的天就连边,之后暴力跑网络流。

这样显然会TTT但是这是一个正解的启示。

首先我们先拓宽一下hallhallhall定理,原来是2×k2\times k2×k个点的二分图匹配左边任意kkk个点都和右边至少kkk个点匹配是这张图有完全匹配的充要条件,但是不难发现如果右边多加了几个点显然还是满足条件的。

所以上面那个可以理解为左边任意kkk个点都和右边至少有kkk条连边,然后不难发现段其实只有2n2^n2n种不同的,所以我们直接二分一个答案然后设fsf_sfs表示包含集合sss的段匹配了多少个点。

如果fs<∣s∣×kf_s<|s|\times kfs<s×k就无解了,然后fsf_sfs要用高维前缀和或者FWTFWTFWT做就好了。

时间复杂度O(2nnlog⁡nk)O(2^nn\log nk)O(2nnlognk)


code

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=18; ll n,k,a[N],bit[1<<N],f[1<<N],s[N*200000]; bool check(ll t){ll MS=(1<<n);memset(f,0,sizeof(f));for(ll i=1;i<=t;i++)f[MS-1]++,f[s[i]^(MS-1)]--;for(ll i=0;i<n;i++)for(ll j=0;j<MS;j++)if(j&(1<<i))f[j^(1<<i)]+=f[j];for(ll i=0;i<MS;i++)if(f[i]<bit[i]*k)return 0;return 1; } signed main() {scanf("%lld%lld",&n,&k);for(ll i=0;i<n;i++)scanf("%lld",&a[i]);ll MS=(1<<n),L=2*n*1e5;for(ll i=1;i<MS;i++)bit[i]=bit[i-(i&-i)]+1;for(ll i=1;i<=L;i++)for(ll j=0;j<n;j++)if((i-1)%(a[j]<<1)<a[j])s[i]|=(1<<j);ll l=1,r=L;while(l<=r){ll mid=(l+r)>>1;if(check(mid))r=mid-1;else l=mid+1;}printf("%lld\n",l);return 0; }

总结

以上是生活随笔为你收集整理的ARC106E-Medals【hall定理,高维前缀和】的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。