link
大意:
思路:
先来考虑单个数字的情况
其实首先可以将题意稍微转化一下,就是移动一个石子的代价是其移动的距离。这样的话,显然我们的策略就是对于每一个石子,一次性将其移动到正确的位置,毕竟能一步到位为什么要分两次呢。在此基础上,我们要做的就是选择一个最终的合并的位置,使得移动的总距离最小
这个的结论还是比较显然的,就是所以石子位置的中位数。
接下来看看多个数字的问题
显然我们不可能对于每一个数字都去找它的最佳位置。但是如果已经知道最佳位置的话,利用数位dp,我们可以很快知道转移到该位置的总代价。那么不妨来看看最终位置转移的时候需要改变什么。如果统计转移的代价可以接受的话,这题就有突破口了,毕竟总位数不会很多。
假设当前在位置pos,当pos+1的时候,显然总代价改变的值det就是:位置>pos的数字个数-位置<=pos的数字个数,当pos右移的时候,前者的值单调增加,后者的值单调减小,所以这个改变的值det也是单调增加的。所以我们可以得出一个递推来找到最佳位置的策略:pos从1开始向右推,如果右移一位的总代价改变值<0,也就是总代价可以变小,就右移一位,否则就break即可。
这个转移其实也不难统计,就是维护一下每一个数字在对应位置的贡献即可。这里dfs2统计的是从上一位转移过来后对总代价的减小值。如果结果大于0的话,就说明减小值为正,否则我们就让其与0取最大即可,相当于不转移。
ll dfs2(ll x,ll head,ll pos,ll sum)
{
if(sum<0) return 0;
if(x==0) return max(sum,0ll);
if(!head&&dp[x][sum]!=-1) return dp[x][sum];
ll tot=0;
ll lim=head?a[x]:k-1;
for(int i=0;i<=lim;++i)
{
tot+=dfs2(x-1,head&&i==lim,pos,sum+((x<pos)?-i:i));
}
if(!head) dp[x][sum]=tot;
return tot;
}
但是这里sum,也就是变小的贡献,在统计过程中是有可能小于0的。为了防止越界,我们可以在一开始就判断一下,如果sum<0,就return 0.合理性在于,我们的数位dp是从高位往低位做的,也就是说sum是先增后减的,如果sum已经小于0了,它也不可能再变大了。
最后,我们只要统计出转移到第一个位置的总代价,然后向右递推即可。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
const ll mod=1000007;
ll n,m,k;
ll a[60];
ll cnt=0;
ll dp[60][4010];
ll init_dfs(ll x,ll head,ll sum)
{
if(x==0) return sum;
if(!head&&dp[x][sum]!=-1) return dp[x][sum];
ll tot=0;
ll lim=head?a[x]:k-1;
for(int i=0;i<=lim;++i)
{
tot+=init_dfs(x-1,head&&i==lim,sum+i*(x-1));
}
if(!head) dp[x][sum]=tot;
return tot;
}
ll dfs2(ll x,ll head,ll pos,ll sum)
{
if(sum<0) return 0;
if(x==0) return max(sum,0ll);
if(!head&&dp[x][sum]!=-1) return dp[x][sum];
ll tot=0;
ll lim=head?a[x]:k-1;
for(int i=0;i<=lim;++i)
{
tot+=dfs2(x-1,head&&i==lim,pos,sum+((x<pos)?-i:i));
}
if(!head) dp[x][sum]=tot;
return tot;
}
ll f(ll x)
{
memset(dp,-1,sizeof dp);
cnt=0;
while(x)
{
a[++cnt]=x%k;
x/=k;
}
ll ans=init_dfs(cnt,1,0);
for(int i=2;i<=cnt;++i)
{
memset(dp,-1,sizeof dp);
ll det=dfs2(cnt,1,i,0);
if(det) ans-=det;
else break;
// ans-=dfs2(cnt,1,i,0);
}
return ans;
}
void solve()
{
cin>>n>>m>>k;
cout<<f(m)-f(n-1)<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// ll t;cin>>t;while(t--)
solve();
return 0;
}