方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。
说来也巧,位置在 i i i 的人面前的第 j j j 堆的石子的数量,刚好是 i i i 写成 K K K 进制后的第 j j j 位。现在方伯伯要玩一个游戏,商场会给方伯两个整数 L , R L,R L,R。
方伯伯要把位置在 [ L , R ] [L, R] [L,R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。
商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。例如: 10 10 10 进制下的位置在 12312 12312 12312 的人,合并石子的最少代价为: 1 ∗ 2 + 2 ∗ 1 + 3 ∗ 0 + 1 ∗ 1 + 2 ∗ 2 = 9 1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9 1∗2+2∗1+3∗0+1∗1+2∗2=9即把所有的石子都合并在第三堆
输入仅有 1 1 1 行,包含 3 3 3 个用空格分隔的整数 L , R , K L,R,K L,R,K,表示商场给方伯伯的 2 2 2 个整数,以及进制数
输出仅有 1 1 1 行,包含 1 1 1 个整数,表示最少的代价。
3 8 3
5
1 ≤ L ≤ R ≤ 1 0 15 , 2 ≤ K ≤ 20 1 \le L \le R \le 10^{15}, 2 \le K \le 20 1≤L≤R≤1015,2≤K≤20
wtcl, 这道题都想了好久…
显然还是数位 d p dp dp。 注意到对于一个有 k k k位数的 P P P, 如果把所有数位的数字数都放在第 m m m位, 那么贡献是 ∑ i = 1 k ∣ i − m ∣ × d g t [ i ] \sum_{i=1}^{k}|i-m|\times dgt[i] ∑i=1k∣i−m∣×dgt[i]。
那么我们可以先把所有数放在最后一位, 然后逐位向前移。 从第 i i i位移到第 i − 1 i-1 i−1位的减少贡献就是前 i − 1 i-1 i−1位的数位和减去后 K − i + 1 K-i+1 K−i+1位的数位和。 如果这个值小于0, 直接 r e t u r n return return就好。
由于 K ≤ 20 K\le 20 K≤20, 所以所有数位和不会太大, 直接设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i位数位和为 j j j 的贡献之和就好了。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
int num[70];
int K;
ll l, r;
ll dp[50][2050];
ll DFS(R int dgt, R int sum, R bool lim)
{
if (!dgt) return sum;
if ((!lim) && (~dp[dgt][sum])) return dp[dgt][sum];
int up = lim ? num[dgt] : K - 1;
ll ret = 0;
for (R int i = 0; i <= up; ++i)
ret += DFS(dgt - 1, sum + (dgt - 1) * i, lim & (i == up));
if (!lim) dp[dgt][sum] = ret;
return ret;
}
ll DFS(R int dgt, R int sum, R int pos, R bool lim)
{
if (sum < 0) return 0;
if (!dgt) return sum;
if ((!lim) && (~dp[dgt][sum])) return dp[dgt][sum];
int up = lim ? num[dgt] : K - 1;
ll ret = 0;
for (R int i = 0; i <= up; ++i)
if (dgt >= pos) ret += DFS(dgt - 1, sum + i, pos, lim & (i == up));
else ret += DFS(dgt - 1, sum - i, pos, lim & (i == up));
if (!lim) dp[dgt][sum] = ret;
return ret;
}
ll solve(R ll val)
{
int cnt = 0;
std::memset(dp, -1, sizeof(dp));
W (val) num[++cnt] = val % K, val /= K;
ll ret = DFS(cnt, 0, 1);
for (R int i = 2; i <= cnt; ++i)
std::memset(dp, -1, sizeof(dp)), ret -= DFS(cnt, 0, i, 1);
return ret;
}
int main(void)
{
scanf("%lld%lld%d", &l, &r, &K);
printf("%lld", solve(r) - solve(l - 1));
}