题目描述
方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。
说来也巧,位置在 (i) 的人面前的第 (j) 堆的石子的数量,刚好是 (i) 写成 (K) 进制后的第 (j) 位。现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 (L,R)。
方伯伯要把位置在 ([L, R]) 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 (× imes) 移动的距离。
商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。例如:(10) 进制下的位置在 (12312) 的人,合并石子的最少代价为:(1 imes 2 + 2 imes 1 + 3 imes 0 + 1 imes1 + 2 imes 2 = 9)即把所有的石子都合并在第三堆。
输入格式
输入仅有 (1) 行,包含 (3) 个用空格分隔的整数(L,R,K),表示商场给方伯伯的 (2) 个整数,以及进制数。
输出格式
输出仅有 (1) 行,包含 (1) 个整数,表示最少的代价。
输入输出样例
输入 #1
3 8 3
输出 #1
5
说明/提示
对于 (100\%) 的数据,(1 le L le R le 10^{15}, 2 le K le 20)
分析
对于一堆石子来说,显然合并到石子位置的中位数是最优的
但是题中给出的石子达到了 (10^{15}) ,暴力枚举显然是不现实的
但是把所有的石子都合并到同一个位置的贡献是可以计算的
所以我们考虑先把所有的石子合并到编号为 (1) 的一堆
然后再把石子从左往右移,每次算出减小的贡献减去
如果当前的位置在修改的位置左边,那么减小的贡献减去当前的数字
否则减小的贡献加上当前的数字
因为递归时从编号较大的石子递归到编号较小的石子
所以减小的贡献一定是先增大后减小的
因此为了防止数组越界,当当前值小于 (0) 时,直接 (return 0)
代码
#include<cstdio>
#include<cstring>
#define ll long long
#define rg register
const int maxk=65,maxm=1e4+5;
ll f[maxk][maxm],l,r;
int num[maxk],cnt,k;
ll dfs(ll ws,ll tot,bool lim){
if(!ws) return tot;
if(!lim && f[ws][tot]!=-1) return f[ws][tot];
int up=lim?num[ws]:(k-1);
long long ans=0;
for(int i=0;i<=up;i++){
ans+=dfs(ws-1,tot+i*(ws-1),lim && i==up);
}
if(!lim) f[ws][tot]=ans;
return ans;
}
ll dfs2(ll ws,ll tot,int xg,bool lim){
if(tot<0) return 0;
if(!ws) return tot;
if(!lim && f[ws][tot]!=-1) return f[ws][tot];
int up=lim?num[ws]:(k-1);
long long ans=0;
for(int i=0;i<=up;i++){
ans+=dfs2(ws-1,tot+(ws<xg?-i:i),xg,lim && i==up);
}
if(!lim) f[ws][tot]=ans;
return ans;
}
ll solve(ll now){
cnt=0;
while(now){
num[++cnt]=now%k;
now/=k;
}
memset(f,-1,sizeof(f));
long long nans=dfs(cnt,0,1);
for(int i=2;i<=cnt;i++){
memset(f,-1,sizeof(f));
nans-=dfs2(cnt,0,i,1);
}
return nans;
}
int main(){
scanf("%lld%lld%d",&l,&r,&k);
printf("%lld
",solve(r)-solve(l-1));
return 0;
}