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