SCOI2014 方伯伯的商场之旅
方伯伯的商场之旅
方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。说来也巧,位置在 \(i\) 的人面前的第 \(j\) 堆的石子的数量,刚好是 \(i\) 写成 \(K\) 进制后的第 \(j\) 位。
现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 \(L,R\)。方伯伯要把位置在 \([L, R]\) 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量\(\times\)移动的距离。商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。
例如:若要合并十进制下的位置在 \(12312_{(10)}\) 的人面前的石子,所需的最少代价为:
\(1 \times 2 + 2 \times 1 + 3 \times 0 + 1 \times 1 + 2 \times 2 = 9\)。
即把所有的石子都合并在第三堆时代价最少。
对于所有的数据,\(1 \leq L \leq R \leq 10^{15},\ 2 \leq K \leq 20\)。
题解
这个最优点不能直接找出来。但是注意到这个最优点本质上是带权重心,所以可以沿用调整法。
具体而言,我们一开始把集结点设为\(1\)求个和。然后枚举集结点从\(i\)挪动到\(i+1\),如果挪动能使答案减小就把变化量加到答案里面;如果不能就不计入答案。
int64 L,R,dp[51][2],f[51][2],g[51][1000][2];
int K,n,a[51];
int64 dfs(int n,bool flag){ // move to 1
if(!n){
f[0][flag]=1;
return 0;
}
if(dp[n][flag]!=-1) return dp[n][flag];
int64 val=0,cnt=0;
int up=flag?a[n]:K-1;
for(int i=0;i<=up;++i){
val+=dfs(n-1,flag and i==up)+f[n-1][flag and i==up]*i*(n-1);
cnt+=f[n-1][flag and i==up];
}
f[n][flag]=cnt;
return dp[n][flag]=val;
}
int64 calc(int n,int v,int pos,bool flag){ // move from pos-1 to pos
if(!n) return v;
if(g[n][v][flag]!=-1) return g[n][v][flag];
int64 ans=0;
int up=flag?a[n]:K-1,c=n>=pos?1:-1;
for(int i=0;i<=up;++i){
if(v+c*i<0) break;
ans+=calc(n-1,v+c*i,pos,flag and i==up);
}
return g[n][v][flag]=ans;
}
int64 solve(int64 x){
if(!x) return 0;
for(n=0;x;x/=K) a[++n]=x%K;
memset(dp,-1,sizeof dp);
int64 ans=dfs(n,1);
for(int i=2;i<=n;++i){
memset(g,-1,sizeof g);
ans-=calc(n,0,i,1);
}
return ans;
}
int main(){
read(L),read(R),read(K);
printf("%lld\n",solve(R)-solve(L-1));
return 0;
}
静渊以有谋,疏通而知事。