2019.03.28 bzoj3598: [Scoi2014]方伯伯的商场之旅(带权中位数+数位dp)

时间:2023-03-09 01:41:50
2019.03.28 bzoj3598: [Scoi2014]方伯伯的商场之旅(带权中位数+数位dp)

传送门

题意咕咕咕自己读吧挺简单的


思路:

由带权中位数的性质可以得到对于每个数放在每个二进制位的代价一定是个单调或者单峰函数,因此我们先把所有的数都挪到第一个位置,然后依次向右枚举峰点(极值点)把能挪的挪走即可。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
int a[62],len=0;
ll f[62][1205],L,R;
int K;
inline ll dfs(int pos,int sum,bool lim){
    if(pos>len)return sum;
    if(!lim&&~f[pos][sum])return f[pos][sum];
    ll ret=0;
    for(ri i=0,up=lim?a[pos]:K-1;i<=up;++i)ret+=dfs(pos+1,sum+i*(pos-1),lim&&i==up);
    if(!lim)f[pos][sum]=ret;
    return ret;
}
inline ll calc(int pos,int sum,int mid,bool lim){
    if(pos>len)return max(sum,0);
    if(!lim&&~f[pos][sum])return f[pos][sum];
    ll ret=0;
    for(ri i=0,up=lim?a[pos]:K-1;i<=up;++i)ret+=calc(pos+1,sum+(pos<mid?-i:i),mid,lim&&i==up);
    if(!lim)f[pos][sum]=ret;
    return ret;
}
inline ll solve(ll x){
    len=0;
    while(x)a[++len]=x-x/K*K,x/=K;
    reverse(a+1,a+len+1);
    memset(f,-1,sizeof(f));
    ll ret=dfs(1,0,1);
    for(ri i=2;i<=len;++i)memset(f,-1,sizeof(f)),ret-=calc(1,0,i,1);
    return ret;
}
int main(){
    cin>>L>>R>>K;
    cout<<solve(R)-solve(L-1);
    return 0;
}