[CF1854E] Game Bundles

发布时间 2023-10-04 21:14:29作者: 灰鲭鲨

题目描述

Rishi is developing games in the 2D metaverse and wants to offer game bundles to his customers. Each game has an associated enjoyment value. A game bundle consists of a subset of games whose total enjoyment value adds up to $ 60 $ .

Your task is to choose $ k $ games, where $ 1 \leq k \leq 60 $ , along with their respective enjoyment values $ a_1, a_2, \dots, a_k $ , in such a way that exactly $ m $ distinct game bundles can be formed.

输入格式

The input is a single integer $ m $ ( $ 1 \le m \le 10^{10} $ ) — the desired number of game bundles.

输出格式

The first line should contain an integer $ k $ ( $ 1 \le k \le 60 $ ) — the number of games.

The second line should contain $ k $ integers, $ a_1, a_2, \dots, a_k $ ( $ 1 \le a_1, a_2, \dots, a_k \le 60 $ ) — the enjoyment values of the $ k $ games.

首先对于一个选择的集合,大于 30 的数最多只会选择一次。

所以考虑随机一个只由小于等于 30 的数组成的数列,然后在后面增加大于 30 的数。

首先想到到不断在后面增加小于等于 30 的数。每增加一个就是一下可不可以跑出来答案。

怎么试呢?定义 \(dp_i\) 为组成 \(i\) 的方案数,那么增加某一个大于 30 的数 \(x\) 增加的方案数是 \(dp_{60-x}\)。把所有的 dp 值从大到小排序后,能选就选。

然后发现这种方式凑出来的 \(m\) 不能超过 \(10^8\),但是正确率很高。

考虑为什么凑出来方案数太少,这是因为小于 30 的数重复的太少。

枚举一个 \(lim\),然后新增的时候强制序列中的数不超过 \(lim\),就能过了。

#include<bits/stdc++.h>
using namespace std;
int a[65],id[65];
long long n,m,dp[65];
int T,fl;
mt19937 gen(time(0));
int cmp(int x,int y)
{
	return dp[x]>dp[y];
}
int main()
{
	scanf("%lld",&n);
	while(1)
	{
		for(int j=30;j;j--)
		{
			memset(dp,0,sizeof(dp));
			dp[0]=1;
			for(T=1;T<=60;T++)
			{
				a[T]=gen()%j+1;
				for(int i=60;i>=a[T];i--)
					dp[i]+=dp[i-a[T]];
				if(dp[60]>n)
					break;
				m=n-dp[60],fl=T;;
		//		printf("%d\n",m);
				for(int i=0;i<=29;i++)
					id[i]=i;
				sort(id,id+30,cmp);
				for(int i=0;i<=29;i++)
				{
					while(fl<=60&&m>=dp[id[i]])
						a[++fl]=60-id[i],m-=dp[id[i]];
				}
				if(!m&&fl<=60)
				{
					printf("%d\n",fl);
					for(int i=1;i<=fl;i++)
						printf("%d ",a[i]);
					exit(0);
				}
			}
		}
	}
}