P3286 [SCOI2014]方伯伯的商场之旅(数位dp)

思路:
先固定集合点在最低位(第一位),很容易用数位 d p dp dp求出来所有数的贡献,然后考虑集合点往高位移动移位的贡献改变,显然变化量是集合点左边的数位之和-减去右边的数位之和。也就是前缀和-后缀和,又因为,前缀和是不断递增的,后缀和是不断递减的(从左往右看),所以答案的函数是满足单调性的,即当变化量为负就移动,否则此时一定是最优情况。

注意开 l o n g   l o n g long\ long long long,不然泪两行,还有空间要开足够。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105,M=5005,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
ll dp[N][M];
int a[N],k;
ll l,r;
ll dfs(int x,ll s,int p,bool li){
	if(!x) return max(s,0LL);
	if(!li&&~dp[x][s]) return dp[x][s];
	int mx=li?a[x]:k-1;
	ll ans=0;
	for(int i=0;i<=mx;i++)
		ans+=dfs(x-1,s+(p==1?i*(x-1):(x<p?-i:i)),p,li&(i==mx));
	if(!li) dp[x][s]=ans;
	return ans;
}
ll fun(ll n){
	int w=0;ll ans=0;
	while(n) a[++w]=n%k,n/=k;
	for(int i=1;i<=w;i++)
		mst(dp,-1),ans+=(i==1?1:-1)*dfs(w,0,i,1);
	return ans;
}
int main(){
	scanf("%lld%lld%d",&l,&r,&k);
	printf("%lld\n",fun(r)-fun(l-1));
	return 0;
}