luogu P3286

問題の意味

牙ボボは1日モール整理のゲームに移動します。モールは、行のいくつかの労働者を送りました。誰もが石のいくつかの山の前に持っていました。

私はK-バンドを書いた後、それはとても、jはちょうど最初のビットである私のj番目の位置の前で、人々の石のヒープの数が起こりました。今牙ボボは牙ボボ二つの整数のL、Rを与えるモールショッピング、ゲームをプレイするために

石のヒープに組み合わされる[L、R]は牙ボボは、各石の位置に置かれます。各操作は、彼が距離を旅*モバイル石の数を犠牲にして、別の山に山にいくつかの石を移動する人、目の前にある石の2つの山を選択することができます。

モールのコミットメント、長いタスクを完了するように牙ボボは、彼にいくつかのココナッツ、小さな価格、彼はより多くのココナッツを与えるを与えます。だから、牙ボボ非常に心配して、私はあなたがどのくらいの最低費用を彼に伝えたいです。たとえば、10進12312人の位置、石の最小値をマージするコスト: 1 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9 1 * 0 * 1 2 + 2 + 3 + 1×1 + 2×2 = 9 すなわち、すべての石は第3の反応器に結合され

データ範囲

ここに画像を挿入説明

ソリューション

参考ブログ

まず、デジタルデータDPのように見えた範囲、及び各桁のために、観察によって見つけることができ、合わせ位置のコストが組み合わされた単語の関数として示されている場合(Y軸を組み合わせ、x軸のコストが結合位置である)を形成、凸包の形に成長する。
その後、最良の答えは、DPを持つすべての桁数考慮して決定されます。
番号が最初にすべての数字は、コストの最初の位置にある見つけ、次いで第2の計算最初に置かれている何を変更値が同じでない場合、それぞれの数であるように考慮すべきことは、より良い位置になる(プレフィックスと-とサフィックス)。ので、暴力的列挙接頭辞、接尾辞、およびその後、そのような数字のDPアレイの数に注意してくださいこれは、転送することができます。

これは、今、i番目の位置にあること、およびデジタルプリ前に、SUF後、この数は転送時に列挙次を埋めるためにどのような位置、x未満であることを意味要件DPの配列です。

#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