题解(5031. 【NOI2017模拟3.27】B)(数论,组合数学)

题目:

n,k<=1e5;

官方题解写的好像是狄利克雷卷积快速幂,可惜太菜了没看出来.

考场想了个比较奇特的方法,考虑枚举ik,即对于每个f(ik)考虑它被计算了多少次

因为ik一定是i的约数,所以可以考虑枚举i的约数

于是问题就被转化为最后一个数给定,前面每个数都是后一个数的倍数的序列有多少个,设对于x有dp(x),把k当做常数

乍一看很不可做,但如果对一个数质因数分解,我们可以用到倍数的性质,一个数x是另一个数y的倍数,当且仅当每个质因子的指数都满足(cx >= cy)

所以不妨令t = i / ik, 对t质因数分解,设t = ∏pici ,则dp(ik) = ∏C(ci+k,ci),为什么呢?因为既然质因子要满足单调不减,我们就可以把质因子的序列当做无序,因为只要种类和个数相同,排序成单调不减就相同

于是变成k个无差别小球扔进(ci + 1)(指数可以是0)个不同的盒子,允许有空,隔板法插一插

时间复杂度记忆化一下大概就是O(n*sqrt(n/logn))(除以一个log是因为我线性筛预处理了质数);

(多测不清空,暴零两行泪)

代码如下

/*B*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
int f[maxn],g[maxn];
int jc[maxn],invjc[maxn];
vector<int>pr[maxn];
int dp[maxn];
int prime[maxn],v[maxn],m;
int qpow(int x,int y){
	int ans = 1;
	while(y){
		if(y & 1)		ans = 1ll * ans * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return ans;
}
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')	c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
void Shpi(int n){
	for(int i = 2; i <= n; ++i){
		if(!v[i]){
			v[i] = i;
			prime[++m] = i;
		}
		for(int j = 1; j <= m; ++j){
			if(v[i] < prime[j] || i * prime[j] > n)		break;
			v[i * prime[j]] = prime[j];
		}
	}
}
int C(int x,int y){
	return 1ll * jc[x] * invjc[y] % mod * invjc[x - y] % mod;
}
int sol(int x,int k){
	if(dp[x] != -1)		return dp[x];
	int idx = x;
	dp[idx] = 1;
	int i;
	for(i = 1; i <= m; ++i){
		int v = prime[i];
		int cnt = 0;
		if(v * v > x)	break;
		while(x % v == 0){
			cnt++;
			x /= v;
		}
		dp[idx] = 1ll * dp[idx] * C(cnt+k,cnt) % mod;
	}
	if(x != 1)
		dp[idx] = 1ll * dp[idx] * C(k+1,1) % mod;
	return dp[idx];
}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	jc[0] = 1;
	Shpi(1e5);
	for(int i = 1; i <= 1e5; ++i)
		jc[i] = 1ll * jc[i-1] * i % mod;
	invjc[100000] = qpow(jc[100000],mod-2);
	for(int i = 1e5 - 1; i >= 0; i--)
		invjc[i] = 1ll * invjc[i+1] * (i + 1) % mod;
	for(int i = 1; i <= 1e5; ++i){
		for(int j = 1; j * i <= 1e5; ++j)
			pr[i * j].push_back(i);
	}
	int t = read();
	while(t--){
		int n = read(),k = read();	
		memset(dp,-1,sizeof(dp));
		for(int i = 1; i <= n; ++i)		f[i] = read();
		for(int i = 1; i <= n; ++i){
			g[i] = 0;
			for(int j = 0; j < (int)pr[i].size(); ++j){
				int x = pr[i][j];
				g[i] = (1ll * f[x] * sol(i/x,k-1) + g[i]) % mod;
			}
		}
		for(int i = 1; i <= n; ++i)
			printf("%d ",g[i]);
		puts("");
	}
	return 0;
}

  


 


 

posted @ 2020-08-02 10:08  y_dove  阅读(178)  评论(0编辑  收藏  举报