http://www.lydsy.com/JudgeOnline/problem.php?id=3598
能做的最后一道方伯伯~~
看了一下,一开始以为是区间dp,后来才知道是数位dp.
考虑这样一种做法,先枚举当前石子是全部搬到mid处最优,然后计算,这样貌似对于每一个数都需要枚举。
那么假设我们处理搬到mid处最优的数有多少个,能计算出来,答案也就出来了。
怎么搞呢?貌似不太好搞。
参考了省队rank3的思想。
首先初始把所有石子搬到1,算出代价,这一般不是最优解。
考虑把把搬到i的所有石子,转而搬到i+1,这样的代价是 sum[i] - 后缀(i+1) 如果这个<0,就更优。
于是我们考虑枚举i从1到n-1,计算从i搬到i+1能优化的代价有多少。
可以用dp[pos][change]表示处理了前pos位且所有的数前面pos<极限,这样的所有数在i搬到i+1的时候 优化了change ,最后能优化的总代价。
(有点晕,还是看代码,自己理解吧~!)
简单点就是 对于每一次i搬到i+1考虑前pos位已经优化了change,计算出当前状态后续拓展状态一共能优化多少。 记录的时候不要触界(比如前3为极限123,你考虑第3位只能维护<122的,因为如果你达到123,后面有的数本来越界了但是没考虑到,就多计算了) 其实也可以多开一维,dp[pos][change][bool p] p表示是否触界。
但是这样新增一维空间就变大了(虽然不开时间可能会加长~~~~)
然后计算一下就好了。
(我的数位dp版本和网上大神的不太一样,我的bool a表示是否在界内,a=1表示前面的已经比极限小了,可以枚举到上限K-1 ;a=0表示刚好触界。下位只能枚举到bit[n])
(有的地方用ll没考虑清楚,估计爆int了; 不想调,一键改成了ll,可能慢了一些,但还是A了。)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define ll long long
using namespace std;
ll L,R,K;
ll pos[52];
ll dp[52][1600],num[52][2];//num[i][j]表示前i位 是否越界的数字有多少
//dp表示代价 首先全部为1时代价
ll dfs(ll n,bool a)
{
if(!n)return num[n][a]=1,0;//到最后
if(~dp[n][a])return dp[n][a];
ll ed=a?K-1:pos[n];
ll &f=dp[n][a]=0,&sum=num[n][a]=0;
for(ll i=0;i<=ed;i++)
{
f+=dfs(n-1,a||i<pos[n])+num[n-1][a||i<pos[n]]*i*(n-1);
sum+=num[n-1][a||i<pos[n]];
}
return f;
}
ll dfs2(ll n,ll change,ll mid,bool a)//dp[i][j]表示前i位,优化了change的能优化多少
{
if(!n)return change;
if(a&&(~dp[n][change]))return dp[n][change];
ll ed=a?K-1:pos[n];
ll ff=(n>mid)?1:-1;
ll ans=0;
for(ll i=0;i<=ed;i++)
{
ll next=change+(ll)i*ff;
if(next<0)break;
ans+=dfs2(n-1,next,mid,a||i<pos[n]);
}
if(a)dp[n][change]=ans;
return ans;
}
ll solve(ll x)
{
ll i;
for(i=1;;i++)
{
if(x)
{
pos[i]=x%K;
x/=K;
}
else break;
}
i--;
memset(dp,-1,sizeof(dp));
ll res=dfs(i,0);
for(ll j=1;j<i;j++)
{
memset(dp,-1,sizeof(dp));
res-=dfs2(i,0,j,0);
}
return res;
}
int main()
{
scanf("%lld%lld%lld",&L,&R,&K);
printf("%lld\n",solve(R)-solve(L-1));
return 0;
}