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;
}

posted on 2020-06-10 10:08  autoint  阅读(153)  评论(0编辑  收藏  举报

导航