【校内模拟】锁

校内传送门

没有标签是因为我真的不知道这算什么类型

题面描述

这题我说不来大意你们还是看题面描述吧

小Z住的房子一共有n个人,他们每人有一个重要度。

房子的门上可以装若干把锁。

假设共有k把锁,命名为1到k。每把锁有一种对应的钥匙,也用1到k表示。

钥匙可以复制若干份并发给任意多个居民。每个人都可以持有若干钥匙,可以不持有钥匙。

如果几名居民钥匙的并集是全集,他们都在场时就能打开房门。

房东规定,一组居民都在场时能打开房门当且仅当他们的重要度加起来至少为m。

问至少需要给房间装多少把锁。即,求最小的k,使得可以适当地给居民们每人若干钥匙,使得任意一组重要度之和小于m的居民持有的钥匙不能打开所有房门,使得任意一组重要度之和大于等于m的居民持有的钥匙能打开所有房门。

样例输入
4 3
1 1 1 1

样例输出
6

对于前30%的测试数据,满足所有ai=1。

对于另外30%的测试数据,1≤n≤8。

对于100%的测试数据,1≤n≤20,1≤m≤1e9,任意居民重要度≤m。

题解

第一眼看不出来是什么题。于是跑去做T3

看到20想了状压DP,但不知道该描述什么状态以及怎么转移。后来对着样例手玩了20分钟,终于第一次找到正确的方案(没错我样例都看不懂),然后由于我画出那个方案的方式非常特别,跟组合数学完全一致,于是用组合数过掉了前三十分。

30pts

公式就是 C m 1 n C_{m-1}^n 。至于原因……花二十分钟画出样例找规律?

100pts

正解就是推理,看完之后恍然大悟。推理如下:
{
答案是这样的居民子集个数x:重要度的和不足m,但加入任何一个新居民都将导致重要度的和大于等于m。

必要性:由于上面的集合重要度都不够,他们都至少缺一把锁。若不足x 把锁,这些子集中必有两个x,y 缺同一把锁l。把这两个子集x 和y 并起来,仍然缺l 这把锁,无法开门但现在子集的重要度已经达到n 了,与题目要求矛盾。

充分性:一共x 把锁,每把锁上面各写一个这种居民的子集(互不相同)。一个居民持有所
有上面的子集不包括自己的锁的钥匙,这样满足要求。

注意到如果所有人加起来重要度都不够,则需要一把锁,无人有钥匙。
}

所以暴力一点跑个dfs就过去了~(当然这个dfs是可以优化的)

#include<bits/stdc++.h>
#define rint register int
#define iint inline int
#define ivoid inline void
#define endll '\n'
#define ll long long
using namespace std;
const int N=2e6+5;
const int M=3e3+5;
const int inf=0x3f3f3f3f;
int m,n,q,k,x,y,z,u,v,w,s,t,l,r;
ll a[21];
int vis[21];
int sum,cnt,ans,res,num,tot,mx;
iint rad()
{
	int x=0,f=1;char c;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*f;
}

ivoid dfs(int x,ll sum)
{
	if(x==n+1){
		if(sum>=m)return;
		for(rint i=1;i<=n;i++){
			if(!vis[i]&&a[i]+sum<m)return;
		}ans++;return;
	}
	vis[x]=1;
	dfs(x+1,sum+a[x]);
	vis[x]=0;
	dfs(x+1,sum);
}


int main()
{
//	freopen("lock.in","r",stdin);
//	freopen("lock.out","w",stdout);
	n=rad();m=rad();
	for(rint i=1;i<=n;i++)a[i]=rad();
	dfs(1,0);
	cout<<ans;
	return 0;
}
发布了44 篇原创文章 · 获赞 16 · 访问量 7262

猜你喜欢

转载自blog.csdn.net/Cyan_rose/article/details/88078170
今日推荐