zoukankan      html  css  js  c++  java
  • 【bzoj3598】: [Scoi2014]方伯伯的商场之旅

    Description

    方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。
    现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。
    例如:10 进制下的位置在 12312 的人,合并石子的最少代价为:
    1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9
    即把所有的石子都合并在第三堆

    Input

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

    Output

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

    Sample Input

    3 8 3

    Sample Output

    5

    HINT

     1 < =  L < =  R < =  10^15, 2 < =  K < =  20

    题解:

      据说是数位DP水题,EXM?

      一开始想了个5维DP……想了想不太对,怂了题解……

      先强制性让集合点为最低位,然后得到一个答案,但显然这个不是最优解,那么考虑当某个数集合点从低位转移到高一位的要求,即此位以前数字之和要大于后面数之和,若用$a_{i}(P)$表示P进制下第i位的数字,这个条件就是:$sum_{x=i+1}^n a_{x}(P)>=sum_{x=1}^{i-1}a_{x}(P)$。在考虑如何DP。

      首先对于强制性选择最低位,可以直接数位DP,这部分很裸;接着,考虑从次低位到最高位为集合点的减少量,记忆化搜索的时候我们传一个选取位置,当当前位数大于等于此值时我们加上此位枚举值,反之减去,若减去到某位后和小于了0,那么说明这个状态不满足进位集合,直接返回0即可。最后我们用第一次DP出的答案减去之后枚举新集合点的减少量即为答案。

      (话说第一次打记忆化搜索,感觉怪怪的。)

    代码(抄来的233):

       

     1 #define Troy 10/11/2017
     2 
     3 #include <bits/stdc++.h>
     4 
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 
     9 ll f[100][9*20*20],P;
    10 
    11 int num,p[100];
    12 
    13 inline ll dfs(int pos,int sum,bool limit){
    14     if(pos==0)  return sum;
    15     if(!limit&&f[pos][sum]!=-1) return f[pos][sum];
    16     int end=limit?p[pos]:P-1;
    17     ll ret=0;
    18     for(int i=0;i<=end;i++)
    19         ret+=dfs(pos-1,sum+(pos-1)*i,limit&&i==end);
    20     if(!limit)  f[pos][sum]=ret;
    21     return ret;
    22 }
    23 
    24 inline ll dfs(int pos,int up,int sum,bool limit){
    25     if(sum<0)   return 0;
    26     if(pos==0)  return sum;
    27     if(!limit&&f[pos][sum]!=-1) return f[pos][sum];
    28     int end=limit?p[pos]:P-1;
    29     ll ret=0;
    30     for(int i=0;i<=end;i++)
    31         if(pos>=up) ret+=dfs(pos-1,up,sum+i,limit&&i==end);
    32         else    ret+=dfs(pos-1,up,sum-i,limit&&i==end);
    33     return  limit==0?f[pos][sum]=ret:ret;
    34 }
    35 
    36 inline ll calc(ll n){
    37     num=0;
    38     do{ 
    39         p[++num]=n%P;
    40         n/=P;
    41     }while(n);
    42     memset(f,-1,sizeof(f));
    43     ll ret=dfs(num,0,true);
    44     for(int i=2;i<=num;i++)
    45         memset(f,-1,sizeof(f)),ret-=dfs(num,i,0,true);
    46     return ret;
    47 }
    48 
    49 int main(){
    50     ll a,b;
    51     scanf("%lld%lld%lld",&a,&b,&P);
    52     printf("%lld
    ",calc(b)-calc(a-1));
    53 }   
  • 相关阅读:
    父子组件的数据传递
    前端处理:elementUI 表格索引代表第几条数据
    qt的moc,uic,rcc命令的使用
    支持无限精度无限大数的类BigNumber实现
    二进制的妙用
    C#实现任意大数的计算和简单逻辑命题的证明——前言
    打磨程序员的专属利器——文本
    打磨程序员的专属利器——快捷键
    打磨程序员的专属利器——命令行&界面
    Linux命令全集
  • 原文地址:https://www.cnblogs.com/Troywar/p/7652774.html
Copyright © 2011-2022 走看看