【洛谷3286】[SCOI2014] 方伯伯的商场之旅(数位DP)

题目链接

  • 给定一个正整数 (k),编号为 (i) 的人面前的第 (j) 堆石子中的石子数恰好等于 (i)(k) 进制下的第 (j) 位的值。
  • 把某一个人面前的第 (x) 堆石子完全合并入第 (y) 堆石子需要的代价为 (x) 中的石子数 ( imes|x-y|)
  • 求将 ([L,R]) 中所有人面前的石子分别合并为一堆所需要的最小代价和。
  • (1le Lle Rle10^{15})(2le kle20)

数位 DP 求负变化量

对于一个人,我们的策略肯定选择 石子位置的中位数 作为最终的集合位置,但这样并不方便求解。

考虑将最终的集合位置从最高位向最低位移动,在这一过程中代价的变化量逐渐增大,所以当代价变化量由负转正时就可以停止移动了,因为之后不可能再出现新的负变化量。

用数位 DP 来实现对这一过程的维护。即,初始假设最终的集合位置在最高位,并求出所有人的代价和。然后,依次枚举每一个位置 (i),DP 求出最终集合位置从第 (i+1) 位移到第 (i) 位的所有负变化量之和,更新答案。

具体地,对于每个 (i),用记忆化搜索实现,记录 (x,s) 两维状态表示处理完前 (x) 位后代价变化量为 (s) 即可。

代码:(O(klog^4V))

#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 V 5000
#define LL long long
using namespace std;
int k,c,a[60],g[60];LL f[60][V<<1];
I LL DP(CI x,CI s,CI fg=0)//记忆化搜索
{
	if(!x) return max(s,0);if(fg&&~f[x][s+V]) return f[x][s+V];LL t=0;
	RI lim=fg?k-1:a[x];for(RI i=0;i<=lim;++i) t+=DP(x-1,s+g[x]*i,fg||(i^lim));return fg&&(f[x][s+V]=t),t;//枚举这一位填的数
}
I LL Calc(LL n)//计算小于等于n的答案
{
	if(!n) return 0;c=0;W(n) a[++c]=n%k,n/=k;RI j;for(j=1;j<=c;++j) g[j]=c-j;memset(f,-1,sizeof(f));LL t=DP(c,0);//分解;假设最终集合位置在最高位
	for(RI i=c-1;i;memset(f,-1,sizeof(f)),t-=DP(c,0),--i) for(j=1;j<=c;++j) g[j]=j<=i?1:-1;return t;//求出最终集合位置从i+1移到i的负变化量之和
}
int main()
{
	LL L,R;return scanf("%lld%lld%d",&L,&R,&k),printf("%lld
",Calc(R)-Calc(L-1)),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3286.html