luogu P3286

题意

方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。

说来也巧,位置在 i 的人面前的第 j 堆的石子的数量,刚好是 i 写成 K 进制后的第 j 位。现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 L,R。

方伯伯要把位置在 [L, R] 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 * 移动的距离。

商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。例如:10 进制下的位置在 12312 的人,合并石子的最少代价为: 1 2 + 2 1 + 3 0 + 1 1 + 2 2 = 9 1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9 即把所有的石子都合并在第三堆

数据范围

在这里插入图片描述

解法

参考博客

首先数据范围长得很像数位dp,然后经过观察可以发现,对于每个数字,如果将合并的代价和合并的位置表示成函数形式的话(y轴是合并的代价,x轴是合并的位置),会长成一个下凸壳的形式.
然后考虑用数位dp求出所有数的最优答案:
首先先求出所有数都放在第一个位置的代价,然后计算哪些数放在第二个位置代价会更优.这里每个数的变化值是不一样的,是(前缀和-后缀和).所以暴力枚举前缀和,后缀和,dp数组中记这样的数的个数,然后就可以转移了.

现在是要求出这个dp数组,含义是在第i个位置,之前的数位和是pre,之后的是suf,然后这个数要小于x,转移时枚举下一个位置填什么.

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int l,r,n,k,a[65];
int f[65][2],sum[65][2];
int dp[65][255][255][2];//代码里dp数组的含义是前i个位置,总的数字和为j,前面的数字和为k,有没有小于上界的数字数量
int solve(int x){
	int ans=0;n=0;while(x){a[++n]=x%k;x/=k;}reverse(a+1,a+1+n);f[0][1]=1;
	for(int i=0;i<n;i++){
		f[i+1][1]+=f[i][1];sum[i+1][1]+=sum[i][1]+f[i][1]*a[i+1]*i;
		for(int t=0;t<a[i+1];t++)f[i+1][0]+=f[i][1],sum[i+1][0]+=sum[i][1]+f[i][1]*t*i;//数位dp时分有没有小于上界两种情况的转移
		for(int t=0;t<k;t++)f[i+1][0]+=f[i][0],sum[i+1][0]+=sum[i][0]+f[i][0]*t*i;
	}
	ans=sum[n][0]+sum[n][1];
	for(int i=0;i<=n;i++)sum[i][0]=sum[i][1]=0;for(int i=1;i<=n;i++)f[i][0]=f[i][1]=0;
	for(int v=1;v<n;v++){
		dp[0][0][0][1]=1;
		for(int i=0;i<n;i++){
			if(i+1<=v){
				for(int j=0;j<=i*k;j++){
					if(dp[i][j][j][0]==0&&dp[i][j][j][1]==0)continue;
					dp[i+1][j+a[i+1]][j+a[i+1]][1]+=dp[i][j][j][1];
					for(int t=0;t<a[i+1];t++)dp[i+1][j+t][j+t][0]+=dp[i][j][j][1];
					for(int t=0;t<k;t++)dp[i+1][j+t][j+t][0]+=dp[i][j][j][0];
				}
			}
			else{
				for(int j=0;j<=i*k;j++){
					for(int u=0;u<=v*k;u++){
						if(dp[i][j][u][0]==0&&dp[i][j][u][1]==0)continue;
						dp[i+1][j+a[i+1]][u][1]+=dp[i][j][u][1];
						for(int t=0;t<a[i+1];t++)dp[i+1][j+t][u][0]+=dp[i][j][u][1];
						for(int t=0;t<k;t++)dp[i+1][j+t][u][0]+=dp[i][j][u][0];
					}
				}
			}
		}
		for(int pre=0;pre<=v*k;pre++)
		for(int suf=pre+1;suf+pre<=n*k;suf++){
			ans-=(suf-pre)*(dp[n][suf+pre][pre][0]+dp[n][suf+pre][pre][1]);
		//	printf("%lld %lld %lld %lld\n",pre,suf,dp[n][suf+pre][pre][0],dp[n][suf+pre][pre][1]);
		}
		for(int i=0;i<=n;i++)
		for(int j=0;j<=i*k;j++)
		for(int u=0;u<=v*k;u++)dp[i][j][u][0]=dp[i][j][u][1]=0;
	}
	return ans;
}
signed main(){
	l=read(),r=read(),k=read();
	printf("%lld\n",solve(r)-solve(l-1));
	return 0;
}

发布了61 篇原创文章 · 获赞 1 · 访问量 973

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103924640