标签:记忆化 c++ class span script namespace names main 实现
输出仅有 1 行,包含 1 个整数,表示最少的代价。
1 < = L < = R < = 10^15, 2 < = K < = 20
正解:数位$dp$。
数位$dp$基本套路,拆成$[1,l-1]$和$[1,r]$,还有从高位到低位统计贡献。
我们先把所有数都移到第一位的答案统计出来,那么我们只要考虑哪些数需要往高位移。
我们把一个数从第$i-1$位移到第$i$位时候,可以发现,前$i-1$位数字距离$+1$,后面的数字距离$-1$,实际上就是前缀加一次,后缀减一次。
我们考虑每次往后移一位的贡献差,如果这个贡献差大于$0$,那就不用移了,因为这个贡献差其实是一个单峰函数。
那么我们可以先把全放到第一位的贡献算出来,设$f[i][j]$表示前$i$位,$[i+1,n]$位贡献和为$j$的总贡献,可以用数位$dp$搞出来。
然后每次算出贡献差并累加到答案中,状态与上面是一样的,只是转移略有不同。
建议使用记忆化搜索实现,比较好写。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 6 using namespace std; 7 8 ll f[55][3000],l,r; 9 int st[55],n,k; 10 11 il ll dfs(RG int pos,RG ll s,RG int lim){ 12 if (!pos) return s; 13 if (!lim && f[pos][s]!=-1) return f[pos][s]; 14 RG int end=lim ? st[pos] : k-1; RG ll res=0; 15 for (RG int i=0;i<=end;++i) 16 res+=dfs(pos-1,s+i*(pos-1),lim && i==end); 17 if (!lim) f[pos][s]=res; return res; 18 } 19 20 il ll dfs(RG int pos,RG ll s,RG int m,RG int lim){ 21 if (s<0) return 0; if (!pos) return s; 22 if (!lim && f[pos][s]!=-1) return f[pos][s]; 23 RG int end=lim ? st[pos] : k-1; RG ll res=0; 24 for (RG int i=0;i<=end;++i) 25 if (pos>=m) res+=dfs(pos-1,s+i,m,lim && i==end); 26 else res+=dfs(pos-1,s-i,m,lim && i==end); 27 if (!lim) f[pos][s]=res; return res; 28 } 29 30 il ll solve(RG ll x){ 31 RG ll ans; n=0; while (x) st[++n]=x%k,x/=k; 32 memset(f,-1,sizeof(f)),ans=dfs(n,0,1); 33 for (RG int i=2;i<=n;++i) 34 memset(f,-1,sizeof(f)),ans-=dfs(n,0,i,1); 35 return ans; 36 } 37 38 int main(){ 39 #ifndef ONLINE_JUDGE 40 freopen("shop.in","r",stdin); 41 freopen("shop.out","w",stdout); 42 #endif 43 cin>>l>>r>>k; 44 cout<<solve(r)-solve(l-1); 45 return 0; 46 }
标签:记忆化 c++ class span script namespace names main 实现
原文地址:http://www.cnblogs.com/wfj2048/p/7701178.html