把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【UOJ567】【IOI2020】Biscuits(DP)

题目链接

  • 给定一个长度为 \(n\) 的序列 \(a_{0\sim n-1}\),表示有 \(a_i\) 块口味值为 \(2^i\) 的饼干。
  • 你需要选出若干饼干分成 \(m\) 份。
  • 求有多少种不同的 \(t\) 值,使得存在一种每份饼干口味值之和都为 \(t\) 的方案。
  • 数据组数\(\le10^3\)\(1\le n\le 60\)\(1\le \sum a_i\times2^i\le10^{18}\)

动态规划

一个复杂度不够优秀的做法,但毕竟数据范围只有 \(60\),还是足以通过本题的。

考虑如何唯一的表示一种 \(t\) 值。

由于小的可以拼成大的,而大的不能拆成小的,因此我们不妨 从大到小枚举,每次能填就填

然后发现可以划分出若干段,每段中除了最后一个位置以外都是饼干不够分,而最后一个位置则有充足的饼干。

因此设 \(f_i\) 表示最后一段结尾为\(i\)的方案数。

转移时在 \(0\sim i-1\) 中枚举 \(j\) 表示 新一段的开始,为了防止算重,强制第 \(j\) 位为 \(1\) 且第 \(j+1\sim i-1\) 位为 \(0\)

接着从大到小枚举 \(k\) 表示 新一段的结尾,记录这一段中能满足之前所有位置饼干都不够分的最小数 \(s\),维护第 \(k\sim j\) 位的饼干口味值总和 \(t\)(除以 \(2^k\),因为不能算入第 \(0\sim k-1\) 位)。

如果 \(s>\lfloor\frac tm\rfloor\),那么这个位置肯定无法作为这一段的结尾,跳过。

否则,如果 \(\lfloor\frac tm\rfloor<2^{j-k+1}-1\),则这一段中的数实际上可以在 \(s\sim \lfloor\frac tm\rfloor\) 中任选,因此给 \(f_i\) 乘上一个系数 \(\lfloor\frac tm\rfloor-s+1\) 转移到 \(f_k\),然后更新 \(s\)\(\lfloor\frac tm\rfloor+1\)(因为接着处理之后的位就要求这一位饼干不够分)。

不然,这一位肯定有充足的饼干,这一段中的数可以在 \(s\sim 2^{j-k+1}-1\) 中任选,给 \(f_i\) 乘上一个系数 \(2^{j-k+1}-s\) 转移到 \(f_k\),然后结束对 \(k\) 的枚举。

代码:\(O(T60^3)\)

#include "biscuits.h"
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 60
#define LL long long
using namespace std;
int n;LL f[N+5];LL count_tastiness(LL m,vector<LL> a)
{
	RI lim=61-log2(m);memset(f,0,sizeof(f)),a.resize(lim);
	RI i,j,k;LL s,t,ans=0;for(f[i=lim]=1;~i;ans+=f[i--]) for(j=i-1;~j;--j)//枚举上一段的结尾和新一段的开头
		if(a[j]>=m) f[j]+=f[i];else for(s=1,t=a[j],k=j-1;~k;--k) if(s<<=1,(t<<=1)+=a[k],s<=t/m)//更新s,t;s<=t/m才可能作为一段结尾
			if(t/m<(1LL<<j-k+1)-1) f[k]+=f[i]*(t/m-s+1),s=t/m+1;else {f[k]+=f[i]*((1LL<<j-k+1)-s);break;}//根据t/m与2^(j-k+1)-1的大小讨论
	return ans;
}
posted @ 2021-08-18 19:54  TheLostWeak  阅读(98)  评论(0编辑  收藏  举报