【洛谷3286】[SCOI2014] 方伯伯的商场之旅(数位DP)
- 给定一个正整数 \(k\),编号为 \(i\) 的人面前的第 \(j\) 堆石子中的石子数恰好等于 \(i\) 在 \(k\) 进制下的第 \(j\) 位的值。
- 把某一个人面前的第 \(x\) 堆石子完全合并入第 \(y\) 堆石子需要的代价为 \(x\) 中的石子数 \(\times|x-y|\)。
- 求将 \([L,R]\) 中所有人面前的石子分别合并为一堆所需要的最小代价和。
- \(1\le L\le R\le10^{15}\),\(2\le k\le20\)
数位 DP 求负变化量
对于一个人,我们的策略肯定选择 石子位置的中位数 作为最终的集合位置,但这样并不方便求解。
考虑将最终的集合位置从最高位向最低位移动,在这一过程中代价的变化量逐渐增大,所以当代价变化量由负转正时就可以停止移动了,因为之后不可能再出现新的负变化量。
用数位 DP 来实现对这一过程的维护。即,初始假设最终的集合位置在最高位,并求出所有人的代价和。然后,依次枚举每一个位置 \(i\),DP 求出最终集合位置从第 \(i+1\) 位移到第 \(i\) 位的所有负变化量之和,更新答案。
具体地,对于每个 \(i\),用记忆化搜索实现,记录 \(x,s\) 两维状态表示处理完前 \(x\) 位后代价变化量为 \(s\) 即可。
代码:\(O(k\log^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\n",Calc(R)-Calc(L-1)),0;
}
待到再迷茫时回头望,所有脚印会发出光芒