BZOJ3598 - 方伯伯的商场之旅(数位dp)

题意

方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。
现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。
例如:10 进制下的位置在 12312 的人,合并石子的最少代价为:
1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9
即把所有的石子都合并在第三堆

题解

先思考一下如何求最少代价。由于最后要合并为一堆,所以就是求移到哪一堆代价最小。假设pre和suf数组代表前缀和和后缀和。
若当前的目标为i,每当目标右移一格(i+1),对代价的贡献为pre[i] - suf[i + 1]。这个贡献是单调递增的,所以最小代价的位置为贡献刚好由负转正的位置。

这下就有dp的目标了。枚举目标位置,求出最小代价为目标位置的数和代价和,最后累加答案即可。状态为当前位置p,贡献sdif,目标位置的数字d(用于判断是否是临界位置,防止重复计数)。

细节详见代码。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb push_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 3e5 + 10;
const double eps = 1e-5;
typedef pair<ll, ll> PII;

ll f[40][8000][40];
ll cnt[40][8000][40];
int vis[40][8000][40];
int di[20];
int tar, tag, k;

PII dfs(int p, int sdif, int d, int lmt) {
    if(!p) {
        
        if(sdif <= 0 && 2 * d + sdif > 0) return mp(1, 0);
        return mp(0, 0);
    }
    if(!lmt && vis[p][sdif + 4000][d] == tag) 
        return mp(cnt[p][sdif + 4000][d], f[p][sdif + 4000][d]);
    ll res = 0;
    ll ct = 0;
    int maxx = lmt ? di[p] : (k - 1);
    for(int i = 0; i <= maxx; i++) {
        if(p > tar) {
            auto v = dfs(p - 1, sdif + i, i, i == maxx && lmt);
            ct += v.first;
            res += v.second + v.first * abs(tar - p) * i;
        } else {
            if(p == tar) {
                auto v = dfs(p - 1, sdif - i, i, i == maxx && lmt);
                ct += v.first;
                res += v.second + v.first * abs(tar - p) * i;
            } else {
                auto v = dfs(p - 1, sdif - i, d, i == maxx && lmt);
                ct += v.first;
                res += v.second + v.first * abs(tar - p) * i;
            }
        }
    }
    if(!lmt) {
        vis[p][sdif + 4000][d] = tag;
        f[p][sdif + 4000][d] = res;
        cnt[p][sdif + 4000][d] = ct;
    }
    return mp(ct, res);
}


ll solve(ll x) {
    int tot = 0;
    ll ans = 0;
    while(x) {
        di[++tot] = x % k;
        x /= k;
    }
    for(tar = 1; tar <= tot; tar++) {
        tag++;
        ans += dfs(tot, 0, 0, 1).second;
    }
    return ans;
}

int main() {
    IOS;
    ll l, r;
    cin >> l >> r >> k;
    cout << solve(r) - solve(l - 1) << endl;
}
posted @ 2020-08-20 20:39  limil  阅读(130)  评论(0编辑  收藏  举报