题目链接:点击这里
题目大意:
给定 n , k n,k n,k 计算函数:
f ( n , k ) = { 1 i f k = 0 ∑ i = 1 n ⌈ l o g 2 i ⌉ f ( ⌊ n i ⌋ , k − 1 ) i f k ≥ 1 f(n,k)= \begin{cases} 1 &if\ k=0\\ \sum_{i=1}^n\lceil log_2i \rceil f(\lfloor \frac ni \rfloor,k-1) &if\ k \ge 1 \end{cases} f(n,k)={
1∑i=1n⌈log2i⌉f(⌊in⌋,k−1)if k=0if k≥1
答案对 1 e 9 + 7 1e9+7 1e9+7 取模
题目分析:
分析这个式子显然有 f ( 1 , k ) = 0 ( k ≥ 1 ) f(1,k)=0(k \ge 1) f(1,k)=0(k≥1)
发现 f ( ⌊ n i ⌋ , k − 1 ) f(\lfloor \frac ni \rfloor,k-1) f(⌊in⌋,k−1) 中的 ⌊ n i ⌋ \lfloor \frac ni \rfloor ⌊in⌋ 是一个典型的分块式,可以分块处理,而且 ⌊ n i ⌋ = 1 \lfloor \frac ni \rfloor=1 ⌊in⌋=1 这个块大小为 n 2 \frac n2 2n ,利用这个性质我们可以得到 f ( n , k ) f(n,k) f(n,k) 中 f ( 1 , x ) f(1,x) f(1,x) 的数目近似为 l o g 2 n log_2n log2n (调和级数)
因此对于 n ≥ l o g 2 ( 1 e 8 ) ≥ 27 n \ge log_2(1e8) \ge 27 n≥log2(1e8)≥27 的数据答案就是 0 0 0
对于 n ≤ l o g 2 ( 1 e 8 ) n \le log_2(1e8) n≤log2(1e8) 的情况我们可以枚举 ⌈ l o g 2 i ⌉ \lceil log_2i \rceil ⌈log2i⌉ 然后利用分块处理 f ( ⌊ n i ⌋ , k − 1 ) f(\lfloor \frac ni \rfloor,k-1) f(⌊in⌋,k−1) ,记忆化+递归求解即可
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
int res = 0,flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
if(ch == '-') flag = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
{
res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
ch = getchar();
}
return res*flag;
}
const int maxn = 5e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
unordered_map<int,int>mp[27];
int f(int n,int k)
{
if(k >= 27) return 0;
if(mp[k].find(n) != mp[k].end())
return mp[k][n];
if(k == 0) return mp[k][n] = 1;
else {
int& tmp = mp[k][n];
for(int i = 1;(1<<i) <= 2*n;i++)
{
int l = (1<<(i-1))+1,r = min(1<<i,n),rr;
for(int j = l;j <= r;j = rr+1)
{
int pos = n/j;
rr = min(r,n/pos);
tmp = (tmp+1ll*i*(rr-j+1)%mod*f(pos,k-1))%mod;
}
}
return tmp;
}
}
int main()
{
int n = read(),k = read();
printf("%d\n",f(n,k));
return 0;
}