1 条题解

  • 0
    @ 2025-8-24 21:47:19

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Midoria7
    真的假的

    搬运于2025-08-24 21:47:19,当前版本为作者最后更新于2020-08-20 19:34:22,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    思路

    广告:希丰展

    如果你做过这道题的话可以想到选择石子位置的中位数一定是最优的。

    但是本题对于并无卵用,因为无法同时处理很多数字,只能单个处理。(建议写暴力

    对于本题的时空限制来说显然枚举每个数是不现实的。但显然数位数不会很多,可以考虑枚举贪心,整体处理(前缀和的形式)。首先把第一个位置当作集合点,算出一个贡献。然后向右枚举,更新贡献。

    可以看出这是一个单峰函数,当集合点右移的时候,如果新的答案更小,就更新答案,否则就不变。

    举个栗子:把二号点转移到三号点,变动的就是:小于等于二号点的石子个数 - 大于等于三号点的石子个数。如果这个值是负的,我们就更新。

    为什么贪心是正确的?因为前面的石子数量和一直在增加,而其后的一直在减小,因此答案的减小值一定是不断减小的。

    先用一遍数位 DPDP 求出集合点在一号点的答案,然后向右枚举转移即可。当枚举到 nn 的时候就结束了。

    代码

    不会真的有人数位 DP 写递推吧,不会吧不会吧不会吧。

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    int L,R,K;
    int a[100],f[100][10000];
    
    int dfs(int now,int sum,int p,int lim){
        if(!now)return max(sum,0LL)
        if(!lim&&~f[now][sum])return f[now][sum];
        int ans=0;
        int num=lim?a[now]:K-1;
        for(int i=0;i<=num;i++)
            ans+=dfs(now-1,sum+(p==1?i*(now-1):(now<p?-i:i)),p,lim&&(i==num));
        if(!lim)f[now][sum]=ans;
        return ans;
    }
    
    inline int Solve(int x){
        int n=0;
        while(x){
            a[++n]=x%K;
            x/=K;
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            memset(f,-1,sizeof(f));
            ans+=((i==1)?1:-1)*dfs(n,0,i,1);
        }
        return ans;
    }
    
    signed main(){
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
        scanf("%lld%lld%lld",&L,&R,&K);
        printf("%lld",Solve(R)-Solve(L-1));
        return 0;
    }
    
    • 1

    信息

    ID
    2359
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者