这道题的数位dp是很显然的。然而,本题不仅要计数还要保证最优化,这使得我们难以得到一个简单的dp状态表示方式。
遗憾的是考虑dp状态数的直接减少是一个错误的思考方向。本人在此浪费了几个小时的时间。
注意到虽然是最优化,但决策数是非常少的,仅有O(logn)级别。同时,我们可以很容易地判断一个解是不是最优的。
于是,我们可以枚举最终合并到哪一位,然后就很容易了,本人是维护满足这个条件的数的个数和答案的和,这里就不详细讲了。
最后一个问题在于一个数可能在多个位置都是决策最优的。注意到它的充要条件是选择的那一位(设值为x)左右两边sum的差为0或x,于是只要在判断时把闭区间改成半闭半开区间就可以了。
这数位和为sum,则时间复杂度为O(log2n*sum*k)。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef pair<ll,ll> pll; 5 const int N = 55, SUM = 250, K = 25; 6 ll dp[N][SUM][K],l,r,su[N][SUM][K]; 7 int num[N],cur,k,cnt; 8 pll dfs(int pos,int sum,int val,bool lim) { 9 if (pos == 0) return pll(sum <= 2 * val,0); 10 if ((!lim) && (~dp[pos][sum][val])) 11 return pll(dp[pos][sum][val],su[pos][sum][val]); 12 ll ta = 0, tb = 0; 13 pll tmp; 14 for (int i = 0 ; i <= (lim ? num[pos] : k-1) ; ++ i) { 15 if (pos < cur && sum - i <= 0) break; 16 tmp = dfs(pos-1,pos < cur ? sum-i : sum+i,pos == cur ? i : val,lim && (i == num[pos])); 17 ta += tmp.first; 18 tb += tmp.second + tmp.first * abs(pos-cur) * i; 19 } 20 if (!lim) { 21 dp[pos][sum][val] = ta; 22 su[pos][sum][val] = tb; 23 } 24 return pll(ta,tb); 25 } 26 ll solve(ll x) { 27 ll res = 0; 28 cnt = 0; 29 while (x) num[++cnt] = x%k, x /= k; 30 for (cur = 1 ; cur <= cnt ; ++ cur) { 31 memset(dp,-1,sizeof dp); 32 memset(su,-1,sizeof su); 33 res += dfs(cnt,0,0,1).second; 34 } 35 return res; 36 } 37 int main() { 38 scanf("%lld%lld%d",&l,&r,&k); 39 printf("%lld\n",solve(r) - solve(l-1)); 40 return 0; 41 }
小结:对于难维护的东西,可以通过添加额外信息来完成。