题意
方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 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 '
'
#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;
}