bzoj 3598: [Scoi2014]方伯伯的商场之旅【数位dp】

时间:2022-01-20 10:32:41

参考了这个http://www.cnblogs.com/Artanis/p/3751644.html,好像比一般方法好写
大概思想就是先计算出把所有石子都合并到1位置的代价,这样显然有一些是不优的,然后再分别计算把合并到1的石子合并到p,能优化多少
这个计算就是枚举2到tot位,对于每一位计算挪到这位能被优化的数最多能被优化多少,因为合并点右移的代价是sum[w]-(sum[n]-sum[w]),所以只要这个为负数就退出即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
long long l,r,k,f[N][2],sm[N][2],g[N][N],a[N],tot;
long long dp(int w,int lm)
{
    if(w==0)
    {
        sm[w][lm]=1;
        return 0;
    }
    if(f[w][lm])
        return f[w][lm];
    long long r=0,s=0;
    for(int i=0;i<=(lm?a[w]:k-1);i++)
    {
        r+=dp(w-1,lm&&(i==a[w]))+i*(w-1)*sm[w-1][lm&&(i==a[w])];
        s+=sm[w-1][lm&&(i==a[w])];
    }
    sm[w][lm]=s;
    return f[w][lm]=r;
}
long long wk(long long x)
{
    memset(f,0,sizeof(f));
    tot=0;
    while(x)
        a[++tot]=x%k,x/=k;
    return dp(tot,1);
}
long long dfs(int w,int lm,int p,int s)
{
    if(w==0)
        return s;
    if(!lm&&g[w][s])
        return g[w][s];
    long long r=0;
    for(int i=0;i<=(lm?a[w]:k-1);i++)
    {
        long long nw=(w>=p?1:-1)*i+s;
        if(nw<0)
            break;
        r+=dfs(w-1,lm&&i==a[w],p,nw);
    }
    if(lm)
        return r;
    return g[w][s]=r;
}
long long clc(long long x)
{
    tot=0;
    while(x)
        a[++tot]=x%k,x/=k;
    long long r=0;
    for(int i=2;i<=tot;i++)
    {
        memset(g,0,sizeof(g));
        r+=dfs(tot,1,i,0);
    }
    return r;
}
int main()
{
    scanf("%lld%lld%lld",&l,&r,&k);
    printf("%lld\n",wk(r)-wk(l-1)-(clc(r)-clc(l-1)));
    return 0;
}