【SCOI2014】【BZOJ3598】方伯伯的商场之旅(数位dp)

时间:2022-12-16 12:11:37

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3598

题意:

  对于一个数x,它含有一些小石子,每个石子的值为a[i](a[i]为x在k进制下的第i位),选一个石子的位置pos使得sum(a[i] * abs(i-pos))最小。 求出[L,R]中所有数这个值的和。

题解:

  对于一个数,我们枚举最优位置时,可以发现从i->i+1的变化为+pre[i]-suf[i+1](分别为前缀和与后缀和)。

  30%的暴力分,我们枚举[L,R]中的数,然后进行上述操作即可。

  那么100%的分呢?我们考虑将每一个数的位置设为1,答案为a[i]*(i-1),然后数位dp求出初始花费cost。 对于将位置i->i+1的变化,+前i个数 -后i+1个数。我们考虑枚举这个操作,并且求出进行这一操作后对于这些数共计会优多少。

  上面那些看不懂的话来看这里吧_(:з」∠)_。

【SCOI2014】【BZOJ3598】方伯伯的商场之旅(数位dp)【SCOI2014】【BZOJ3598】方伯伯的商场之旅(数位dp)
 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <iostream>
 5 #define LL long long
 6 using namespace std;
 7 const int MaxN = 65;
 8 int u[MaxN], d[MaxN], LEN;
 9 LL l, r, k;
10 LL f[MaxN][2][2], g[MaxN][1600], ans[2333], sum[MaxN][2][2];
11 
12 LL dp(int len, int up, int down){
13     if (!len) { sum[len][up][down] = 1; return 0; }
14     if (f[len][up][down]!= -1) return f[len][up][down];
15     LL ret = 0ll, num = 0ll;
16     int top = up ? u[len] : (int)k-1, bot = down ? d[len] : 0;
17     for (int i = bot; i <= top; i++){
18         int p1 = (i==top)&&up, p2 = i==bot&&down;
19         ret += dp(len-1, p1, p2) + i*(len-1)*sum[len-1][p1][p2];
20         num += sum[len-1][p1][p2];
21     }
22     f[len][up][down] = ret, sum[len][up][down] = num;
23     return ret;
24 }
25 
26 LL calc(LL x, LL y){
27     int len = 0, len2 = 0;
28     while(x){
29         u[++len] = (int) (x%k);
30         x /= k;
31     }
32     while(y){
33         d[++len2] = (int) (y%k);
34         y /= k;
35     }
36     LEN = len;
37     return dp(len, 1, 1);
38 }
39 
40 LL dfs(int len, int up, int down, int p, int s){
41     if (!len) return (LL)s;
42     if (!up && !down && g[len][s] != -1) return g[len][s];
43     LL ret = 0;
44     int top = up ? u[len] :(int)k-1, bot = down ? d[len] : 0;
45     for (int i = bot; i <= top; i++){
46         int x = (len >= p ? 1 : -1), c = i==top&&up, d = i==bot&&down;
47         x = x*i + s;
48         if (x < 0) break;
49         ret += dfs(len-1, c, d, p, x);
50     }
51     if (!up && !down) g[len][s] = ret;
52     return ret;
53 }
54 
55 void Solve(){
56     memset(f, -1ll, sizeof(f));
57     LL ret = calc(r, l);
58     //cout<<ret<<endl;
59     LL dec = 0ll;
60     for (int i = 2; i <= LEN; i++){
61         memset(g, -1ll, sizeof(g)), dec += dfs(LEN, 1, 1, i, 0);
62     }
63     cout<<ret - dec;
64 }
65 
66 int main(){
67     cin>>l>>r>>k;
68     Solve();
69     return 0;
70 }
View Code