• 方伯伯的商场之旅[SCOI2014]


    题目描述

    方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 (i) 的人面前的第 (j) 堆的石子的数量,刚好是 (i) 写成 (K) 进制后的第 (j) 位。

    现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 (L,R)。方伯伯要把位置在 ([L,R]) 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 ( imes) 移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。

    输入格式

    输入仅有一行,包含三个用空格分隔的整数 (L)(R)(K),表示商场给方伯伯的两个整数,以及进制数。

    输出格式

    输出仅有一行,包含一个整数,表示最少的代价。

    题解

    考虑用 (1sim R) 的答案减掉 (1sim L-1) 的答案,即 (operatorname{solve}(R) - operatorname{solve}(L-1))

    考虑 (operatorname{solve}(n)),先计算把每个人的石子都全部合并到第 (1) 堆所需的代价

    这个是可以通过一次数位dp解决的

    ll dfs1(ll d, ll sum, ll lim) {
    	if (!d) return sum;
    	if (!lim && ~f[d][sum]) return f[d][sum];
    	ll ret = 0; int mx = lim ? a[d] : k-1;
    	for (ll i = 0; i <= mx; i++) {
    		ret += dfs1(d-1, sum + i * (d - 1), lim & (i == mx)); 
    		//第d位有i个石子要移动到第1位
    	}
    	if (!lim) f[d][sum] = ret;
    	return ret;
    }
    

    注意到这样一个性质:对于一个人 (x),假设他把所有石子最终全部移到第 (k) 位,我们把 (k) 叫做 (x) 的集合点,这样移动的代价是 (f_x(k)),那么 (f_x) 一定是一个单谷函数

    不妨把 (x) 的最优集合点叫做 (p_x)

    所以我们可以执行这样的操作:

    for i=2~n,每次把所有 "集合点在 (i) 时比在 (i-1) 时更优 (即 (f_x(i-1)>f_x(i)))" 的那些 (x) 的集合点全部从 (i-1) 变为 (i),也就是说让答案减去 (f_x(i-1)-f_x(i))

    这样一来,由于 (f_x) 是单谷函数,那么一个人 (x) 一定在 (i=2sim p_x) 的时候集合点被移动,那么 (x) 的最终代价就会是 (f_x(p_x)),所以这个做法是正确的

    然后考虑如何进行这个"挪动集合点"的操作 其实和上面的那个计算集合点全为1的数位dp区别不大

    ll dfs2(ll d, ll sum, ll p, ll lim) {
    	if (!d) return max(0ll, sum); //如果sum<0则说明不把集合点从p-1挪到p比较优
    	if (!lim && ~f[d][sum]) return f[d][sum];
    	ll ret = 0; int mx = lim ? a[d] : k-1;
    	for (ll i = 0; i <= mx; i++) {
    		ret += dfs2(d-1, sum + (d < p ? -i : i), p, lim & (i == mx));
    	}
    	if (!lim) f[d][sum] = ret;
    	return ret;
    }
    

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll k, len, a[60];
    ll L, R, f[60][5005];
    
    ll dfs1(ll d, ll sum, ll lim) {
    	if (!d) return sum;
    	if (!lim && ~f[d][sum]) return f[d][sum];
    	ll ret = 0; int mx = lim ? a[d] : k-1;
    	for (ll i = 0; i <= mx; i++) {
    		ret += dfs1(d-1, sum + i * (d - 1), lim & (i == mx));
    	}
    	if (!lim) f[d][sum] = ret;
    	return ret;
    }
    
    ll dfs2(ll d, ll sum, ll p, ll lim) {
    	if (!d) return max(0ll, sum);
    	if (!lim && ~f[d][sum]) return f[d][sum];
    	ll ret = 0; int mx = lim ? a[d] : k-1;
    	for (ll i = 0; i <= mx; i++) {
    		ret += dfs2(d-1, sum + (d < p ? -i : i), p, lim & (i == mx));
    	}
    	if (!lim) f[d][sum] = ret;
    	return ret;
    }
    
    ll solve(ll n) {
    	len = 0;
    	ll tmp = n;
    	while (tmp) {
    		a[++len] = tmp % k;
    		tmp /= k;
    	}
    	memset(f, -1, sizeof(f));
    	ll ret = dfs1(len, 0, 1);
    	for (ll i = 2; i <= len; i++) {
    		memset(f, -1, sizeof(f));
    		ret -= dfs2(len, 0, i, 1);
    	}
    	return ret;
    }
    
    int main() {
    	scanf("%lld %lld %lld", &L, &R, &k);
    	printf("%lld
    ", solve(R) - solve(L-1));
    	return 0;
    }
    
  • 相关阅读:
    Android ADB关闭Selinux ( adb shell setenforce 0 )
    通过windowmanager在camera界面上显示内容
    Android Handler类 发送消息-post()和postDelay(), Looper讲解
    Ubuntu下的log日志查看器
    Ubuntu下 安卓 adb 命令报:“insufficient permissions for device: user in plugdev group; ”问题的解决办法
    java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib/libjni_i
    为 Linux 应用程序编写 DLL
    android Camera 之 ZSL
    Android Studio 导入 Android 系统模块并编译和调试
    微服务架构设计
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM102.html
Copyright © 2020-2023  润新知