arc106E

题目大意

有n个人,第i个人有一个ai,表示其第[k(2ai)+1,k(2ai)+ai]天在

每天可以给至多一个人发一枚奖牌,求所有人都发了只少K枚奖牌的最小时间

n<=18,K,ai<=1e5

题解

如果能想到hall定理的话就没了

一个人拆成K个点,二分答案,暴力加边,hall定理判断,要预处理选牌的人的集合

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define low(x) ((x)&-(x))
#define ll long long
//#define file
using namespace std;

int a[19],b[4000001],f[262144],p[19],num[262144],n,K,i,j,k,l,L,R,Mid;

void init()
{
	int s,i,j,k,l;
	p[0]=1;
	fo(i,1,n) p[i]=p[i-1]*2;
	fo(i,1,p[n]-1) num[i]=num[i-low(i)]+1;
	
	fo(i,1,4000000)
	{
		s=0;
		fo(j,1,n)
		if ((i-1)%(a[j]*2)<a[j])
		s+=p[j-1];
		b[i]=s;
	}
}
bool pd(int t)
{
	int i,j,k,l,s;
	
	memset(f,0,sizeof(f));
	fo(i,1,t) ++f[p[n]-1],--f[b[i]^(p[n]-1)];
	fo(i,1,n)
	{
		fo(j,0,p[n]-1)
		if (j&p[i-1])
		f[j-p[i-1]]+=f[j];
	}
	
	fo(i,0,p[n]-1)
	if (num[i]*K>f[i])
	return 0;
	return 1;
}

int main()
{
	#ifdef file
	freopen("e.in","r",stdin);
	#endif
	
	scanf("%d%d",&n,&K);
	fo(i,1,n) scanf("%d",&a[i]);
	init();
	
	L=1,R=4000000;
	while (L<R)
	{
		Mid=(L+R)/2;
		if (!pd(Mid))
		L=Mid+1; else R=Mid;
	}
	printf("%d
",L);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13873708.html