其他分享
首页 > 其他分享> > P3286 [SCOI2014]方伯伯的商场之旅(数位dp)

P3286 [SCOI2014]方伯伯的商场之旅(数位dp)

作者:互联网

目录

Description

将所有属于 \([l,r]\) 上的数 \(x\) 变为 \(k\) 进制,每一个 \(x\) 都可以变为 \(1\) 位数,其花费为 \(\sum{a[i]*dis}\) ,\(a[i]\) 为 \(i\) 位上的值,\(dis\) 为第 \(i\) 位数所移动的距离,求所有 \(x\) 变为 \(1\) 位数的最小值之和

State

\(1<=l<=r<=10^{15}\)

\(2<=k<=20\)

Input

3 8 3

Output

5

Solution

考虑将一个数合并成一个,合并点 \(pos∈[1,n]\),不难发现这是一个凹函数,但是我们需要像前缀和那样来整体处理

由于每一个数的合并点不同,所以枚举合并点是不可行的

但是可以利用凹函数这一个特点,先将 \(1\) 作为合并点,求出总的 \(\sum{a[i]*(i-1)}\), 然后逐渐将合并点后移,减去对应的贡献,比如 \(5678\),初始化的答案为 \(5*3+6*2+7\),之后合并点左移答案变为 \(5*2+6*1+8\),其贡献为 \(-5-6-7+8\),直到贡献大于等于 \(0\) 结束

但是这样好像还是只考虑了一个数的情况,如果一个数最优合并点已经过去,那么让其的贡献为 \(0\) 就可以解决这个问题了


Code

const int N = 3e5 + 5;

    int n, m, _, k;
    int a[N];    
    ll dp[70][2000];

ll dfs1(int pos, int sum, bool top)
{
    if(! pos) return sum;
    if(! top && dp[pos][sum] != -1) return dp[pos][sum];
    int up = k - 1;
    if(top) up = a[pos];
    ll ans = 0;
    for(int i = 0; i <= up; i ++){
        ans += dfs1(pos - 1, sum + i * (pos - 1), top && (i == up));
    }
    if(! top) dp[pos][sum] = ans;
    return ans;
}

ll dfs(int pos, int sum, bool top, int sign)
{
    if(! pos) return max(sum, 0);
    if(! top && dp[pos][sum] != -1) return dp[pos][sum];
    int up = k - 1;
    if(top) up = a[pos];
    ll ans = 0;
    for(int i = 0; i <= up; i ++){
        int id = i;
        if(pos < sign) id *= -1;
        ans += dfs(pos - 1, sum + id, top && (i == up), sign);
    }
    if(! top) dp[pos][sum] = ans;
    return ans;
}

ll solve(ll x)
{
    int tot = 0;
    while(x) a[++ tot] = x % k, x /= k;
    ms(dp, -1);
    ll ans = dfs1(tot, 0, 1);
    for(int i = 2; i <= tot; i ++){
        ms(dp, -1);
        ans -= dfs(tot, 0, 1, i);
    }
    return ans;
}

signed main()
{
    //IOS;
    ll l, r;
    while(~ sll2(l, r)){
        sd(k);
        pll(solve(r) - solve(l - 1));
    }
    //PAUSE;
    return 0;
}

标签:P3286,sum,合并,pos,int,SCOI2014,ll,dp
来源: https://www.cnblogs.com/Segment-Tree/p/15325597.html