题意: 给定区间, 求该区间内满足
-
说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。
-
方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。
>> face <<
Strategy: 数位dp : 首先想一个问题, 对于一个人面前的石子, 怎样抉择才是最优的? 我们考虑"1,2,3", 如果我们把石子都集中在第一堆, 代价是 2 ∗ 1 + 3 ∗ 2 = 8 2*1+3*2 = 8 2∗1+3∗2=8 ,然后我们考虑集中在第二堆, 代价: 1 ∗ 1 + 3 ∗ 1 = 4 1*1 + 3*1 = 4 1∗1+3∗1=4, 第三堆: 2 ∗ 1 + 2 ∗ 1 = 4 2*1+2*1 = 4 2∗1+2∗1=4 好像没啥规律, 不过可以得到启示: 当集合点从第一堆转成第二堆时, 变化可以看成 8 + 1 − 2 − 3 = 4 8+1-2-3= 4 8+1−2−3=4 , 于是可以得到推论, 从第i堆转移到第i+1堆时, 变化量是i的前缀和减去i+1的后缀和, 而且显然, 变换量是单增的(前缀和单增,后缀和单减), 我们可以贪心的判断转移的变化量是不是正数. 于是我们可以先用数位dp把在区间内所有的数集合点在个位数时的花费算出来, 然后数位dfs枚举集合点判断最优
状态: dp[i][j][k]代表搜到第i位,起点为j, 目前的代价是k的状态;
目标: s u m [ r ] − s u m [ l − 1 ] ( s u m [ i ] 代 表 1 i 内 的 所 有 合 法 数 的 个 数 ) sum[r]-sum[l-1](sum[i]代表1~i内的所有合法数的个数) sum[r]−sum[l−1](sum[i]代表1 i内的所有合法数的个数)
边界: 本题无
合法判断: 条件转移合法判断
转移预处理
attention: 开数组的时候注意不要开太大
双倍经验: 贪心 + 双数位 + 进制转换
- 我觉得这题会了才能证明你学过数位dp
@author: jasonleft 记忆化数位
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (ll i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (ll i = (a); i >= (b); --i)
#define _for(i, a, b) for (ll i = (a); i < (b); ++i)
#define _rof(i, a, b) for (ll i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define id(x) ((x + 8))
#define bin(x) cerr << #x << " is " << bitset<15>(x) << endl
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
using namespace std;
const ll maxn = 30;
ll l, r, cnt, k, dp[maxn][maxn][4002], a[maxn];
inline ll dfs(ll cur, ll sum, ll start, bool up)
{
if (cur == 0)
return sum;
ll &t = dp[cur][start][sum];
if (!up && ~t)
return t;
ll ret = 0;
_rep(i, 0, up ? a[cur] : k - 1)
{
if (cur == start && i == 0)
ret += dfs(cur - 1, sum, start - 1, up && i == a[cur]);
else
ret += dfs(cur - 1, sum + (cur - 1) * i, start, up && i == a[cur]);
}
if (!up)
t = ret;
return ret;
}
ll dfs1(ll cur, ll alter, ll pos, bool up)
{
if (alter < 0)
return 0;
if (!cur)
return alter;
ll &t = dp[cur][pos][alter];
if (!up && ~t)
return t;
ll ret = 0;
_rep(i, 0, up ? a[cur] : k - 1)
{
if (cur >= pos)
ret += dfs1(cur - 1, alter + i, pos, up && i == a[cur]);
else
ret += dfs1(cur - 1, alter - i, pos, up && i == a[cur]);
}
if (!up)
t = ret;
return ret;
}
inline ll solve(ll __)
{
cnt = 0;
met(dp, -1);
while (__)
a[++cnt] = __ % k, __ /= k;
ll res = dfs(cnt, 0, cnt, 1);
met(dp, -1);
_rep(i, 2, cnt) //枚举最终优化的位置
{
res -= dfs1(cnt, 0, i, 1);
}
return res;
}
signed main()
{
cin >> l >> r >> k;
cout << solve(r) - solve(l - 1) << endl;
}