题意
称在二进制下 01 01 01 交替且最高位和最低位都是 1 1 1 的数为斑马数 ,如 ( 1 ) 2 = 1 , ( 10101 ) 2 = 21 (1)_2=1,(10101)_2=21 ( 1 ) 2 = 1 , ( 10101 ) 2 = 21 。
对一个数 n n n 来说,定义它的价值 为最小的正整数 p p p ,使得 n n n 能表示成 p p p 个斑马数 (可以相同也可以不同)。
求 [ l , r ] [l,r] [ l , r ] 中价值 为 k k k 的数个数。
l , r , k ≤ 1 0 18 l,r,k\le 10^{18} l , r , k ≤ 1 0 18 。
题解
考虑一个数的最优分解,即分解成最少个斑马数之和。
设第 i i i 个斑马数为 z i z_i z i ,即 z 1 = 1 , z 4 = 85 z_1=1,z_4=85 z 1 = 1 , z 4 = 85 ,分解中第 i i i 个斑马数的次数为 c i c_i c i 。
假设某一个数的最优分解中含有了某个斑马数 z i ( i > 1 ) z_i(i>1) z i ( i > 1 ) 至少 5 5 5 次以上,那么可以发现 5 z i = z i + 1 + 4 z i − 1 5z_i = z_{i+1} + 4z_{i-1} 5 z i = z i + 1 + 4 z i − 1 ,不会使答案变劣。
若含有了 1 1 1 至少 5 5 5 次以上,用 z 2 = 5 z_2=5 z 2 = 5 替换掉会更优。
所以我们得出了第一个结论 :每个数都存在一种最优分解,使得 ∀ i , c i ≤ 4 \forall i, c_i\le 4 ∀ i , c i ≤ 4 。
若 c i = 4 c_i=4 c i = 4 ,且存在 j < i j<i j < i ,使 c j > 0 c_j>0 c j > 0 ,则有等式
{ 4 z i + z j = z i + 1 + 4 z j − 1 ( j > 1 ) 4 z i + z 1 = z i + 1 ( j = 1 ) \begin{cases}
4z_i+z_j =z_{i+1} + 4z_{j-1}\ &(j>1)\\
4z_i+z_1 =z_{i+1}\ &(j=1)\\
\end{cases}
{ 4 z i + z j = z i + 1 + 4 z j − 1 4 z i + z 1 = z i + 1 ( j > 1 ) ( j = 1 )
哪一种情况都不会使答案变劣,所以得出第二个结论 :每个数都存在一种最优分解,使得 c i c_i c i 为一段前缀 0 0 0 (可以没有),一个 4 4 4 (可以没有),若干个 0 ∼ 3 0\sim 3 0 ∼ 3 的数字。用正则表示就是 0*4?[0-3]*
。
接下来我们考虑处理这样的一个问题:每个 z i z_i z i 都只能使用 0 ∼ 3 0\sim 3 0 ∼ 3 次,总共使用了 k k k 个数,得到的和中,有多少在 [ 1 , n ] [1,n] [ 1 , n ] 中。(可以发现这种数没有重复的表示方法)
把 n n n 看作一种 5 ∗ 5* 5 ∗ 进制数,第 i i i 位的值代表 z i z_i z i 的次数,贪心从高往低去取,可以发现 n n n 的每一位都小于等于 4 4 4 。
问题就转化成了,在 5 ∗ 5* 5 ∗ 进制小于等于 n n n 的表示,每一位只能取 0 ∼ 3 0\sim 3 0 ∼ 3 ,数位和为 k k k 的数有多少个。
证明:
证明所有 5 ∗ 5* 5 ∗ 进制下每一位只能取 0 ∼ 3 0\sim 3 0 ∼ 3 ,以 5 5 5 进制比较,比 n n n 小的数实际都比 n n n 小:这是因为有一位比 n n n 小,而 3 ∑ j = 1 i − 1 z j < z i 3\displaystyle\sum_{j=1}^{i-1}z_j < z_i 3 j = 1 ∑ i − 1 z j < z i ,所以成立。
证明所有 5 ∗ 5* 5 ∗ 进制下每一位只能取 0 ∼ 3 0\sim 3 0 ∼ 3 ,以 5 5 5 进制比较,比 n n n 大的数实际都比 n n n 大:这是因为有一位比 n n n 大,而 z i > 3 ∑ j = 1 i − 1 z j z_i > 3\displaystyle\sum_{j=1}^{i-1}z_j z i > 3 j = 1 ∑ i − 1 z j ,所以也成立。
因此看作一个真正的 5 5 5 进制数位 dp 就能解决的。
回到原问题,首先可以前缀和,只需要解决 [ 1 , n ] [1,n] [ 1 , n ] 中的问题就可以了。
由上文的结论二我们知道,我们可以枚举这个数中唯一的 4 4 4 的位置,然后问题转化为:在 5 5 5 进制小于等于 n n n 的表示,每一位只能取 0 ∼ 3 0\sim 3 0 ∼ 3 ,有一段前缀强制是 0 0 0 ,数位和为 k k k 的数有多少个,这也可以数位 dp 轻易完成。
还需要注意,这道题虽然 k ≤ 1 0 18 k\le 10^{18} k ≤ 1 0 18 ,但由结论我们不难看出 k ∼ O ( log n ) k\sim O(\log n) k ∼ O ( log n ) ,所以设一个上限即可,超过上限直接输出 0 0 0 。(取 90 90 90 比较好)
总复杂度 O ( log 2 n ) O(\log^2 n) O ( log 2 n ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> using namespace std;using ll=long long ; vector<ll> p;const int MAX_CNT = 100 ;const int LEN = 31 ; ll mem[LEN][LEN][MAX_CNT+1 ];int num[LEN];ll dfs (int u,int end,int cnt,bool lim) { if (u==end)return cnt==0 ; if (!lim && ~mem[u][end][cnt])return mem[u][end][cnt]; ll ans = 0 ; int L = 0 , R = lim?num[u]:3 ; for (int i=L;i<=min (R,3 );i++){ if (i>cnt)break ; ans += dfs (u-1 ,end,cnt-i,lim&&i==R); } if (!lim)mem[u][end][cnt]=ans; return ans; }ll precalc (ll n,ll end,ll cnt) { if (cnt>MAX_CNT)return 0 ; for (int i=p.size ()-1 ;i>=0 ;i--){ num[i+1 ]=0 ; while (n>=p[i]){ n-=p[i], num[i+1 ]++; } } return dfs (p.size (),end,cnt,1 ); }ll calc (ll n,ll k) { ll ans = 0 ; ans += precalc (n,0 ,k); if (k<4 )return ans; for (int i=0 ;i<p.size ();i++){ if (p[i]*4 <= n) ans+=precalc (n-p[i]*4 , i+1 , k-4 ); } return ans; }void solve () { ll l,r,k; cin>>l>>r>>k; cout << calc (r,k) - calc (l-1 ,k) << '\n' ; }int main () { memset (mem,-1 ,sizeof (mem)); ios::sync_with_stdio (0 );cin.tie (0 ); p.push_back (1 ); while (p.back ()*4 +1 <= 1e18 )p.push_back (p.back ()*4 +1 ); int T; cin>>T; while (T--)solve (); return 0 ; }