【UOJ567】【IOI2020】Biscuits(DP)

点此看题面

  • 给定一个长度为\(n\)的序列\(a_{0\sim n-1}\),表示有\(a_i\)块口味值为\(2^i\)的饼干。
  • 你需要选出若干饼干分成\(m\)份。
  • 求有多少种不同的\(t\)值,使得存在一种每份饼干口味值之和都为\(t\)的方案。
  • 数据组数\(\le10^3\),\(n\le 60,\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 mt\rfloor\)中任选,因此给\(f_i\)乘上一个系数\(\lfloor\frac tm\rfloor-s+1\)转移到\(f_k\),然后更新\(s\)为\(\lfloor\frac mt\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;
}
上一篇:整体二分


下一篇:贪心——cf708b