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

    题意:

    有$10^{15}$个人,第i个人面前有$lceil log_{k}{i} ceil$堆石子,第j堆石子的数量为i写成k进制的第j位。

    对于编号在$[L,R]$之间的人,你希望把每个人的石子合并成一堆。

    移动一次石子的代价是$移动数量 imes 移动距离$,请你求出最小移动代价之和。

    $L,Rleq 10^{15},2leq kleq 20$。

    题解:

    容易发现每个人的移动代价关于合并到哪一堆的函数是一个下凸的单峰函数。

    那么我们其实就可以写一个类似于整体三分的东西。我写了结果调不出来了。

    发现这题复杂度并不高,于是其实可以把整体三分改成直接枚举合并到哪一堆。

    先求出一开始都合并到第一堆的答案,然后将合并堆逐渐右移,考虑代价的变化。

    对于一个数x,设它在k进制下$[1,a]$位的数字和为$pre_{x,m}$,$[a,n]$位的数字和为$suf_{x,m}$。

    那么每当合并堆从第i-1堆移动到第i堆时,x的代价就会增加$pre_{x,i-1}-suf_{x,i}$。

    根据整体三分的思想,如果到第i堆时$pre_{x,i-1}-suf_{x,i}geq 0$,那么x这个数就被丢在第i-1位之前了,它的最优合并堆一定$<i$。

    否则需要将总代价增加$pre_{x,i-1}-suf_{x,i}$(其实是减少),它的最优合并堆仍然$geq i$。

    注意到如果一个数x在某一堆停止移动了,那么在后面的任何一堆它都满足$pre_{x,i-1}-suf_{x,i}geq 0$,所以无需特判。

    我们现在只需要算这两个东西:

    1. 令$x={a_1 a_2 cdots a_n}_{(k)}$,求$sum limits_{x=L}^{R}{a_2 + 2a_3 +cdots +(n-1)a_n}$。
    2. 对于第p堆,求$sum limits_{x=L}^{R}{min(pre_{x,p-1}-suf_{x,p},0)}$。

    容易想到数位dp就是用来求形如$sum limits_{x=L}^{R}{val_{x}}$且$val_{x}$仅与数位数字有关的式子,于是数位dp即可。

    复杂度$O(300(log_{k}{R})^{2}k)$。(对于所有的数位dp,其复杂度为$O(状态数 imes 转移数)$)

    套路:

    • 形如$sum limits_{x=L}^{R}{val_{x}}$且$val_{x}$仅与数位数字有关的式子$ ightarrow$数位dp。
    • 数位dp的特点:
    1. 如果暴力枚举相当于在遍历一棵树的叶子,只不过这棵树的叶子太多而且有很多相同的子树,于是可以把相同的子树缩成一个点。所以数位dp严格来说就是个优化计数,每个节点的$val$代表原树上某一类的叶子$val$之和。
    2. 写成循环也就是处理出位数在$[1,n-1]$之间的答案,然后对于位数为n的顶着上界的数枚举断点单独算,只不过写起来有点麻烦而已。
    • 求若干个单峰函数的极值之和$ ightarrow$整体三分。

    代码:

    #include<bits/stdc++.h>
    #define maxn 65
    #define maxm 3005
    #define inf 0x7fffffff
    #define ll long long
    #define rint register ll
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    ll k,f[maxn][2][2],g[maxn][maxm][2],dig[maxn];
    
    inline ll read(){
        ll x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline void dfs1(ll n,ll ise){
        if(n==0) f[n][ise][0]=1,f[n][ise][1]=0;
        if(f[n][ise][0]==-1){
            f[n][ise][0]=f[n][ise][1]=0;
            for(ll i=0;i<=(ise?dig[n]:(k-1));i++){
                ll nise=ise&(i==dig[n]);
                dfs1(n-1,nise),f[n][ise][0]+=f[n-1][nise][0];
                f[n][ise][1]+=f[n-1][nise][1]+f[n-1][nise][0]*i*(n-1); 
            }
        }
    }
    
    inline void dfs2(ll n,ll p,ll sum,ll ise){
        if(n==0) g[n][sum+250][ise]=max(sum,0ll);
        if(g[n][sum+250][ise]==-1){
            g[n][sum+250][ise]=0;
            for(ll i=0;i<=(ise?dig[n]:(k-1));i++){
                ll nsum=(n<p)?(sum-i):(sum+i);
                ll nise=ise&(i==dig[n]);
                dfs2(n-1,p,nsum,nise);
                g[n][sum+250][ise]+=g[n-1][nsum+250][nise];
            }
        }
    }
    
    inline ll solve(ll x){
        ll tp=x,n=0;
        while(tp) dig[++n]=tp%k,tp/=k;
        memset(f,-1,sizeof(f));
        dfs1(n,1); ll cost=f[n][1][1];
        for(ll i=2;i<=n;i++){
            memset(g,-1,sizeof(g));
            dfs2(n,i,0,1),cost-=g[n][250][1];
        }
        return cost;
    }
    
    int main(){
        ll l=read(),r=read();k=read();
        cout<<solve(r)-solve(l-1)<<endl;
        return 0;
    }
    方伯伯的商场之旅
  • 相关阅读:
    div与>div区别小结
    自定义动画方法animate
    字符串与json之间的相互转化
    onclick事件与onserverclick事件
    jQuery实现隐藏标签
    CS0016: 未能写入输出文件“c:WindowsMicrosoft.NETFramework64v4.0.30319Temporary ASP.NET Fileshelloiisceb8cab34db603d8App_global.asax.gr73hi-k.dll”--“拒绝访问。 ”
    关于迭代器中IEnumerable与IEnumerator的区别
    C#中部分方法返回值类型为什么只能是void?
    抽象函数与虚函数
    括号配对问题
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13299054.html
Copyright © 2011-2022 走看看