标签:display code += none inf through line gif lap
题意:
有$10^{15}$个人,第i个人面前有$\lceil log_{k}{i}\rceil$堆石子,第j堆石子的数量为i写成k进制的第j位。
对于编号在$[L,R]$之间的人,你希望把每个人的石子合并成一堆。
移动一次石子的代价是$移动数量\times 移动距离$,请你求出最小移动代价之和。
$L,R\leq 10^{15},2\leq k\leq 20$。
题解:
容易发现每个人的移动代价关于合并到哪一堆的函数是一个下凸的单峰函数。
那么我们其实就可以写一个类似于整体三分的东西。我写了结果调不出来了。
发现这题复杂度并不高,于是其实可以把整体三分改成直接枚举合并到哪一堆。
先求出一开始都合并到第一堆的答案,然后将合并堆逐渐右移,考虑代价的变化。
对于一个数x,设它在k进制下$[1,a]$位的数字和为$pre_{x,m}$,$[a,n]$位的数字和为$suf_{x,m}$。
那么每当合并堆从第i-1堆移动到第i堆时,x的代价就会增加$pre_{x,i-1}-suf_{x,i}$。
根据整体三分的思想,如果到第i堆时$pre_{x,i-1}-suf_{x,i}\geq 0$,那么x这个数就被丢在第i-1位之前了,它的最优合并堆一定$<i$。
否则需要将总代价增加$pre_{x,i-1}-suf_{x,i}$(其实是减少),它的最优合并堆仍然$\geq i$。
注意到如果一个数x在某一堆停止移动了,那么在后面的任何一堆它都满足$pre_{x,i-1}-suf_{x,i}\geq 0$,所以无需特判。
我们现在只需要算这两个东西:
容易想到数位dp就是用来求形如$\sum \limits_{x=L}^{R}{val_{x}}$且$val_{x}$仅与数位数字有关的式子,于是数位dp即可。
复杂度$O(300(\log_{k}{R})^{2}k)$。(对于所有的数位dp,其复杂度为$O(状态数\times 转移数)$)
套路:
代码:
#include<bits/stdc++.h> #define maxn 65 #define maxm 3005 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll k,f[maxn][2][2],g[maxn][maxm][2],dig[maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } inline void dfs1(ll n,ll ise){ if(n==0) f[n][ise][0]=1,f[n][ise][1]=0; if(f[n][ise][0]==-1){ f[n][ise][0]=f[n][ise][1]=0; for(ll i=0;i<=(ise?dig[n]:(k-1));i++){ ll nise=ise&(i==dig[n]); dfs1(n-1,nise),f[n][ise][0]+=f[n-1][nise][0]; f[n][ise][1]+=f[n-1][nise][1]+f[n-1][nise][0]*i*(n-1); } } } inline void dfs2(ll n,ll p,ll sum,ll ise){ if(n==0) g[n][sum+250][ise]=max(sum,0ll); if(g[n][sum+250][ise]==-1){ g[n][sum+250][ise]=0; for(ll i=0;i<=(ise?dig[n]:(k-1));i++){ ll nsum=(n<p)?(sum-i):(sum+i); ll nise=ise&(i==dig[n]); dfs2(n-1,p,nsum,nise); g[n][sum+250][ise]+=g[n-1][nsum+250][nise]; } } } inline ll solve(ll x){ ll tp=x,n=0; while(tp) dig[++n]=tp%k,tp/=k; memset(f,-1,sizeof(f)); dfs1(n,1); ll cost=f[n][1][1]; for(ll i=2;i<=n;i++){ memset(g,-1,sizeof(g)); dfs2(n,i,0,1),cost-=g[n][250][1]; } return cost; } int main(){ ll l=read(),r=read();k=read(); cout<<solve(r)-solve(l-1)<<endl; return 0; }
标签:display code += none inf through line gif lap
原文地址:https://www.cnblogs.com/YSFAC/p/13299054.html