zoukankan      html  css  js  c++  java
  • 洛谷 P3286 [SCOI2014]方伯伯的商场之旅

    题目描述

    方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。

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

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

    商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。例如:(10) 进制下的位置在 (12312) 的人,合并石子的最少代价为:(1 imes 2 + 2 imes 1 + 3 imes 0 + 1 imes1 + 2 imes 2 = 9)即把所有的石子都合并在第三堆。

    输入格式

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

    输出格式

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

    输入输出样例

    输入 #1

    3 8 3

    输出 #1

    5

    说明/提示

    对于 (100\%) 的数据,(1 le L le R le 10^{15}, 2 le K le 20)

    分析

    对于一堆石子来说,显然合并到石子位置的中位数是最优的

    但是题中给出的石子达到了 (10^{15}) ,暴力枚举显然是不现实的

    但是把所有的石子都合并到同一个位置的贡献是可以计算的

    所以我们考虑先把所有的石子合并到编号为 (1) 的一堆

    然后再把石子从左往右移,每次算出减小的贡献减去

    如果当前的位置在修改的位置左边,那么减小的贡献减去当前的数字

    否则减小的贡献加上当前的数字

    因为递归时从编号较大的石子递归到编号较小的石子

    所以减小的贡献一定是先增大后减小的

    因此为了防止数组越界,当当前值小于 (0) 时,直接 (return 0)

    代码

    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define rg register
    const int maxk=65,maxm=1e4+5;
    ll f[maxk][maxm],l,r;
    int num[maxk],cnt,k;
    ll dfs(ll ws,ll tot,bool lim){
    	if(!ws) return tot;
    	if(!lim && f[ws][tot]!=-1) return f[ws][tot];
    	int up=lim?num[ws]:(k-1);
    	long long ans=0;
    	for(int i=0;i<=up;i++){
    		ans+=dfs(ws-1,tot+i*(ws-1),lim && i==up);
    	}
    	if(!lim) f[ws][tot]=ans;
    	return ans;
    }
    ll dfs2(ll ws,ll tot,int xg,bool lim){
    	if(tot<0) return 0;
    	if(!ws) return tot;
    	if(!lim && f[ws][tot]!=-1) return f[ws][tot];
    	int up=lim?num[ws]:(k-1);
    	long long ans=0;
    	for(int i=0;i<=up;i++){
    		ans+=dfs2(ws-1,tot+(ws<xg?-i:i),xg,lim && i==up);
    	}
    	if(!lim) f[ws][tot]=ans;
    	return ans;
    }
    ll solve(ll now){
    	cnt=0;
    	while(now){
    		num[++cnt]=now%k;
    		now/=k;
    	}
    	memset(f,-1,sizeof(f));
    	long long nans=dfs(cnt,0,1);
    	for(int i=2;i<=cnt;i++){
    		memset(f,-1,sizeof(f));
    		nans-=dfs2(cnt,0,i,1);
    	}
    	return nans;
    }
    int main(){
    	scanf("%lld%lld%d",&l,&r,&k);
    	printf("%lld
    ",solve(r)-solve(l-1));
    	return 0;
    }
    
  • 相关阅读:
    uboot nand erase 的显示错误修复
    Sougo for linux install.
    S3C6410移植uboot2010.3(2)基本的启动信息修改
    S3C6410移植uboot2010.3(4)uboot的dnw功能添加
    S3C6410移植uboot2010.3(3)正常化配置
    ubuntu乱码修复
    应老婆点(20070705 13:11:34)(新浪)
    克己慎独 2008923 13:32:00 (21ic)
    信任(20061229 14:16:32)(新浪)
    不要轻易承诺 2008926 14:42:00 (21ic)
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/13796213.html
Copyright © 2011-2022 走看看