这算不算是数位DP?
Who Knows。
算法核心: 如果朴素算法最傻逼的那种: 效率:O(R-L1+1)*log(k,r)^2
好的你只能跑20分
那么仔细一看,诶?有人跑了30分暴力! 他们很明显少挂了一个log。
怎么优化? DP优化无外乎这几种,这里就是前缀和。
维护前缀:效率log(k,r); 然后:最重要的一步 ——> (^▽^)! 我们贪心。 先考虑所有数结论点都是1号点。 那么怎么log(k,r)查询?
for(register int i=L+1;i<=R;++i){
++a[1];
int now=1;
while(a[now]==k){
a[now]=0;
++now;
++a[now];
}
if(now>cnt)
cnt=now;
sum[0]=0;
for(int i=1;i<=cnt;i++){//log(k,R)
sum[i]=sum[i-1]+a[i];
}
ll nowsum=0;
for(int i=2;i<=cnt;i++){
nowsum+=(i-1)*a[i];
}
ll last=nowsum;
for(int i=2;i<=cnt;i++){
ll temp=last;
temp+=sum[i-1];
temp-=(sum[cnt]-sum[i-1]);
if(temp<nowsum){
nowsum=temp;
}
last=temp;
if(nowsum==0){
break;
}
}
ans+=nowsum;
}
}
就是上面这段代码。 对于i号节点: 我们会发现移动一位是ansi-1-(sum[n]-sum[i-1])+sum[i-1];
好的,终于少挂了一个log。 那么有没办法让(L-R+1)也挂上log? 答案是有的。
我们考虑容斥原理。
直接数位DP跑出所有在第一位的答案。
然后批量处理。
怎么批量? 答案很简单。 还是一个数位DP,枚举前缀和。
ll st[120]={0};
ll f[120][5000+10]={0};//前pos位,第pos为sum
ll dfs1(ll pos,ll sum,bool flag){
if(pos==0){
return sum;
}
if(!flag&&f[pos][sum])
return f[pos][sum];
ll ans=0;
ll mx;
if(flag){
mx=st[pos];
}
else{
mx=k-1;
}
for(ll i=0;i<=mx;i++){
ans+=dfs1(pos-1,sum+(pos-1)*i,flag&&i==mx);
}
if(!flag){
f[pos][sum]=ans;
}
return ans;
}
可行态是-(sum[n]-sum[i-1])+sum[i-1]<0对吧。 我们再跑一个数位DP就好了 只需要枚举这个前缀和。 什么效率爆炸? 对的这样挂上了很多个log 实际效率是:
e*log(k,R)^4
e是一个常数取决于数据对记忆化的友好程度
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll L,R,k;
ll top;//nom the sum of the log(k,x)
ll st[120]={0};
ll f[120][5000+10]={0};//前pos位,第pos为sum
ll dfs1(ll pos,ll sum,bool flag){
if(pos==0){
return sum;
}
if(!flag&&f[pos][sum])
return f[pos][sum];
ll ans=0;
ll mx;
if(flag){
mx=st[pos];
}
else{
mx=k-1;
}
for(ll i=0;i<=mx;i++){
ans+=dfs1(pos-1,sum+(pos-1)*i,flag&&i==mx);
}
if(!flag){
f[pos][sum]=ans;
}
return ans;
}
ll dfs2(ll pos,ll sum,ll now,bool flag){
if(sum<0){
return 0;
}
if(pos==0){
return sum;
}
if(!flag&&f[pos][sum]){
return f[pos][sum];
}
ll ans=0;
ll mx;
if(flag){
mx=st[pos];
}
else{
mx=k-1;
}
for(ll i=0;i<=mx;i++){
if(pos>=now){
ans+=dfs2(pos-1,sum+i,now,flag&&i==mx);
}
if(pos<now){
ans+=dfs2(pos-1,sum-i,now,flag&&i==mx);
}
}
if(!flag){
f[pos][sum]=ans;
}
return ans;
}
ll solve(ll x){
top=0;
ll ans=0;
while(x){
top++;
st[top]=x%k;
x/=k;
}
memset(f,0,sizeof(f));
ans+=dfs1(top,0,1);
for(ll i=2;i<=top;i++){//枚举i为实际点的值减去这些被耽误的值
memset(f,0,sizeof(f));
ans-=dfs2(top,0,i,1);
}
return ans;
}
int main(){
cin>>L>>R>>k;
cout<<solve(R)-solve(L-1);
return 0;
}