LOJ#3367. 「IOI2020」装饼干

49 篇文章 0 订阅

Description

  • k k k种饼干放入 x x x个袋子之中,使得每一个袋子的饼干价值相同,求有多少种不同的价值能够满足。
  • i i i个饼干有 a i a_i ai个,价值为 2 i − 1 2^{i-1} 2i1.
  • k ≤ 60 , ∑ a i ≤ 1 e 18 k\le60,\sum a_i\le1e18 k60,ai1e18

Solution

  • 很容易想到,贪心放饼干,从高位往低位放,尽量把大的饼干塞入袋子里面,这样一定不劣。
  • 于是就有了一个简单的DP思路,设 f [ i ] [ j ] f[i][j] f[i][j]表示放到第 i i i位,当前小于 i i i的剩下的饼干的和为 j j j
  • 转移的状态数乍一看感觉很多,实际上仔细观察一下:
    f [ i ] [ j ] → f [ i − 1 ] [ m i n ( j − x 2 i − 1 , s i − 2 , 2 i − 1 − 1 ) ] f[i][j]\to f[i-1][min(j-x2^{i-1},s_{i-2},2^{i-1}-1)] f[i][j]f[i1][min(jx2i1,si2,2i11)]
    f [ i ] [ j ] → f [ i − 1 ] [ m i n ( j , s i − 2 , 2 i − 1 − 1 ) ] f[i][j]\to f[i-1][min(j,s_{i-2},2^{i-1}-1)] f[i][j]f[i1][min(j,si2,2i11)]
  • 这种转移的状态实际上是有限的 ,理解上并不太直观。
  • 或者可以考虑另一种方法:
  • 如何判定一个 y y y是否满足条件,从低位往高位做,容易得到
    [ s u m i − 1 x ] ≥ y %   2 i + 1 [\frac{sum_{i-1}}{x}]\ge y\%\ 2^{i+1} [xsumi1]y% 2i+1
  • 那么假设 l i m i = [ s u m i − 1 x ] lim_i=[\frac{sum_{i-1}}{x}] limi=[xsumi1],从高位往低位考虑 y y y的取值,如果当前第 i i i y y y l i m lim lim相同,那么继续递归下去,否则 y < l i m y<lim y<lim,那么限制就转化为了只有 i − 1 i-1 i1的限制,直接从 f i − 1 f_{i-1} fi1转移过来,因此只有 k 2 k^2 k2种状态。
#include "biscuits.h"
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#define ll long long 
#define maxn 63
using namespace std;

map<ll,ll> f[maxn];
ll sum[maxn],X,_2[maxn];
ll dp(int n,ll s){
	if (s/X>=_2[n]-1) s=(_2[n]-1)*X;
	if (f[n].find(s)!=f[n].end()) 	
		return f[n][s];
	if (n==0) return f[n][s]=1;
	ll &tmp=f[n][s];
	f[n][s]=dp(n-1,min(s,sum[n-1]));
	if (s/_2[n-1]>=X)
		f[n][s]+=dp(n-1,min(s-X*_2[n-1],sum[n-1]));
	return f[n][s];
}

ll count_tastiness(ll x, std::vector<ll> a){
	int i,j,k; X=x;
	for(i=0;i<=61;i++) f[i].clear();
	_2[0]=1;for(i=1;i<=61;i++) _2[i]=_2[i-1]<<1;
	for(i=0;i<a.size();i++) 
		sum[i+1]=sum[i]+a[i]*_2[i];
	for(i=a.size();i<=60;i++) sum[i+1]=sum[i];
	return dp(61,sum[a.size()]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值