DP(数位进阶二)

落花浮王杯 提交于 2019-11-26 17:31:59

题意: 给定区间, 求该区间内满足

  • 说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。

  • 方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。

>> face <<

Strategy: 数位dp : 首先想一个问题, 对于一个人面前的石子, 怎样抉择才是最优的? 我们考虑"1,2,3", 如果我们把石子都集中在第一堆, 代价是21+32=82*1+3*2 = 8 ,然后我们考虑集中在第二堆, 代价:11+31=41*1 + 3*1 = 4, 第三堆: 21+21=42*1+2*1 = 4 好像没啥规律, 不过可以得到启示: 当集合点从第一堆转成第二堆时, 变化可以看成8+123=48+1-2-3= 4 , 于是可以得到推论, 从第i堆转移到第i+1堆时, 变化量是i的前缀和减去i+1的后缀和, 而且显然, 变换量是单增的(前缀和单增,后缀和单减), 我们可以贪心的判断转移的变化量是不是正数. 于是我们可以先用数位dp把在区间内所有的数集合点在个位数时的花费算出来, 然后数位dfs枚举集合点判断最优

状态: dp[i][j][k]代表搜到第i位,起点为j, 目前的代价是k的状态;

目标: sum[r]sum[l1](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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!