洛谷傳送門
BZOJ傳送門
題目描述
方伯伯有一天去參加一個商場舉辦的遊戲。商場派了一些工作人員排成一行。每個人面前有幾堆石子。
說來也巧,位置在 的人面前的第 堆的石子的數量,剛好是 寫成 進制後的第 位。現在方伯伯要玩一個遊戲,商場會給方伯兩個整數 。
方伯伯要把位置在 中的每個人的石子都合併成一堆石子。每次操作,他可以選擇一個人面前的兩堆石子,將其中的一堆中的某些石子移動到另一堆,代價是移動的石子數量 * 移動的距離。
商場承諾,方伯伯只要完成任務,就給他一些椰子,代價越小,給他的椰子越多。所以方伯伯很着急,想請你告訴他最少的代價是多少。例如: 進制下的位置在 的人,合併石子的最少代價爲:即把所有的石子都合併在第三堆
輸入輸出格式
輸入格式:
輸入僅有 行,包含 個用空格分隔的整數 ,表示商場給方伯伯的 個整數,以及進制數
輸出格式:
輸出僅有 行,包含 個整數,表示最少的代價。
輸入輸出樣例
輸入樣例#1:
3 8 3
輸出樣例#1:
5
說明
解題分析
wtcl, 這道題都想了好久…
顯然還是數位。 注意到對於一個有位數的, 如果把所有數位的數字數都放在第位, 那麼貢獻是。
那麼我們可以先把所有數放在最後一位, 然後逐位向前移。 從第位移到第位的減少貢獻就是前位的數位和減去後位的數位和。 如果這個值小於0, 直接就好。
由於, 所以所有數位和不會太大, 直接設表示前位數位和爲 的貢獻之和就好了。
代碼如下:
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>
#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));
}