中竞杯校赛 E.数论只会 for 循环 (数论分块 + 记忆化搜索)

题目链接:https://ac.nowcoder.com/acm/contest/9680/E

由定义式可以发现,如果 \(k > log(n)\),那么答案就一定是 \(0\), 所以我们只需要计算 \(k <= 27\) 的答案即可

记忆化搜索,使用 \(unordered_map\) 存储 \(dp\) 数组的答案

对于每个块,如果左端点为 \(l\), 那么对于 \(log(i)\), 右端点 \(r1 = 1 << log(i)\), 对于 \(\frac{n}{i}\),右端点为 \(r2 = \frac{n}{\frac{n}{l}}\)
每次分块的右端点 \(r\)\(r = min(r1, r2)\) 即可,具体实现参考代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<unordered_map> 
using namespace std;
typedef long long ll;

const int M = 1000000007;

int n, k;

unordered_map<int, int> dp[30];

int dfs(int N, int K){
	if(K == 0){
		return dp[K][N] = 1;
	}
	if(dp[K].find(N) != dp[K].end()){
		return dp[K][N];
	}
	
	int res = 0;
	int l, r;
	
	int kk = 0;
	for(l = 2 ; l <= N ;){
		while(1 << (kk + 1) < l) ++kk; 
		if((1 << kk) < l) ++kk;
		
		r = min(min(N / (N / l), (1 << kk)), N);
				
		res = (res + 1ll * (r - l + 1) * kk % M * dfs(N / l, K - 1) % M) % M;
		l = r + 1;
	}
	
	return dp[K][N] = res;
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	n = read(), k = read();
	if(k >= 27) printf("0\n");
	else {
		printf("%d\n", dfs(n, k));
	}
		
	return 0;
}
posted @ 2020-12-20 23:25  Tartarus_li  阅读(65)  评论(0编辑  收藏  举报