• 题解(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;
    }
    

      



  • 相关阅读:
    类型构造器
    WIN32画图窗口
    WIN32创建进程CreateProcess
    WIN通过消息实现互斥同步CreateEvent和SetEvent
    WIN32生产消费经典同步但是以消耗时间为代价
    WIN32互斥体CreateMutex以及限制多开
    WIN32临界区线程锁EnterCriticalSection和LeaveCriticalSection
    GetThreadContext和SetThreadContext
    远程线程注入
    EnumProcessModules 使用 获取进程的路径
  • 原文地址:https://www.cnblogs.com/y-dove/p/13417841.html
Copyright © 2020-2023  润新知