P3286 [SCOI2014]方伯伯的商场之旅(数位dp)
作者:互联网
目录
Description
将所有属于 \([l,r]\) 上的数 \(x\) 变为 \(k\) 进制,每一个 \(x\) 都可以变为 \(1\) 位数,其花费为 \(\sum{a[i]*dis}\) ,\(a[i]\) 为 \(i\) 位上的值,\(dis\) 为第 \(i\) 位数所移动的距离,求所有 \(x\) 变为 \(1\) 位数的最小值之和
State
\(1<=l<=r<=10^{15}\)
\(2<=k<=20\)
Input
3 8 3
Output
5
Solution
考虑将一个数合并成一个,合并点 \(pos∈[1,n]\),不难发现这是一个凹函数,但是我们需要像前缀和那样来整体处理
由于每一个数的合并点不同,所以枚举合并点是不可行的
但是可以利用凹函数这一个特点,先将 \(1\) 作为合并点,求出总的 \(\sum{a[i]*(i-1)}\), 然后逐渐将合并点后移,减去对应的贡献,比如 \(5678\),初始化的答案为 \(5*3+6*2+7\),之后合并点左移答案变为 \(5*2+6*1+8\),其贡献为 \(-5-6-7+8\),直到贡献大于等于 \(0\) 结束
但是这样好像还是只考虑了一个数的情况,如果一个数最优合并点已经过去,那么让其的贡献为 \(0\) 就可以解决这个问题了
Code
const int N = 3e5 + 5;
int n, m, _, k;
int a[N];
ll dp[70][2000];
ll dfs1(int pos, int sum, bool top)
{
if(! pos) return sum;
if(! top && dp[pos][sum] != -1) return dp[pos][sum];
int up = k - 1;
if(top) up = a[pos];
ll ans = 0;
for(int i = 0; i <= up; i ++){
ans += dfs1(pos - 1, sum + i * (pos - 1), top && (i == up));
}
if(! top) dp[pos][sum] = ans;
return ans;
}
ll dfs(int pos, int sum, bool top, int sign)
{
if(! pos) return max(sum, 0);
if(! top && dp[pos][sum] != -1) return dp[pos][sum];
int up = k - 1;
if(top) up = a[pos];
ll ans = 0;
for(int i = 0; i <= up; i ++){
int id = i;
if(pos < sign) id *= -1;
ans += dfs(pos - 1, sum + id, top && (i == up), sign);
}
if(! top) dp[pos][sum] = ans;
return ans;
}
ll solve(ll x)
{
int tot = 0;
while(x) a[++ tot] = x % k, x /= k;
ms(dp, -1);
ll ans = dfs1(tot, 0, 1);
for(int i = 2; i <= tot; i ++){
ms(dp, -1);
ans -= dfs(tot, 0, 1, i);
}
return ans;
}
signed main()
{
//IOS;
ll l, r;
while(~ sll2(l, r)){
sd(k);
pll(solve(r) - solve(l - 1));
}
//PAUSE;
return 0;
}
标签:P3286,sum,合并,pos,int,SCOI2014,ll,dp 来源: https://www.cnblogs.com/Segment-Tree/p/15325597.html