[ASDFZ]复活石

Description

给定\(f(i)\),求\(g(i)=\sum_{i_1|i}\sum_{i_2|i_1}\dots\sum_{i_k|i_{k-1}}f(i_k)(mod\;10^9+7)\).

HINT

\(n,k\leq10^5\)

Solution

30分

\(f[i][j]\)表示选了\(i\)个数,积为\(j\)的总和.

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 10000
#define N 100001
#define T 300000
#define M 1000000007
using namespace std;
typedef long long ll;
int f[2][N],n,m,k;
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){ret=(ret<<1)+(ret<<3)+c-'0';c=getchar();}
	return ret;
}
inline int rd(){
	int ret=0ll;bool f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-') f=0;c=getchar();}
	while(isdigit(c)){ret=(ret<<1)+(ret<<3)+c-'0';c=getchar();}
	if(f) return ret;
	return M-ret;
}
inline void Aireen(){
	m=read();
	while(m--){
		n=read();k=read();
		for(int i=1;i<=n;++i) f[0][i]=rd();
		for(int i=1;i<=k;++i){
			for(int j=1;j<=n;++j){
				f[i&1][j]=0;
				for(int l=sqrt(j);l;--l){
					if(!(j%l)){
						f[i&1][j]+=f[i&1^1][l];
						if(f[i&1][j]>M) f[i&1][j]-=M;
						if(l*l!=j) f[i&1][j]+=f[i&1^1][j/l];
						if(f[i&1][j]>M) f[i&1][j]-=M;
					}
				}
			}
		}
		for(int i=1;i<=n;++i)
			printf("%d ",f[k&1][i]);
		putchar('\n');
	}
}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

100分

inline void sieve(){
	cnt=0;t[1]=1;
	for(int i=1;i<=n;++i) g[i]=0;
	for(int i=2;i<=n;++i){
		if(!g[i]){
			g[i]=i,p[++cnt]=i,t[i]=k;
		}
		for(int j=1,l,q,c,x;j<=cnt&&i*p[j]<=n;++j){
			g[c=i*p[j]]=p[j];
			if(mul[c]) t[c]=C(mul[c]+k-1,k-1); 
			else{
				q=c;t[c]=1;
				while(q>1){
					x=l=g[q];
					while(true){
						if(1ll*x*l>1ll*q||q%(x*l)) break;
						x=x*l;
					}
					q/=x;
					t[c]=1ll*t[c]*t[x]%M; 
				}
			}
			if(!(i%p[j])) break;
		}
	}
}

相当于求每个数分解\(k\)次得到的可重\(i\)\(f(i)\)之和.
实际上\(g(i)\)\(f(j)\;(i|j)\)之间的关系是只与\(k\)有关的.
\(g(i)=t(\frac{i}{j})\times{f(j)}\).
\(t(x)=\begin{cases}1&x=1\\k&x\;is\;prime\\C_{j+k-1}^{k-1}(隔板法)&x=p_i^j\\ \prod_{i=1}^{k}{t(p_k^{a_k})}&x=p_1^{a_1}\;\times\;p_2^{a_2}\;\times\dots\times\;p_k^{a_k}\\\end{cases}\)
上面的式子很好理解,也很好证明,也就是运用了积性函数,隔板法,乘法原理的思想.
至此,问题求解.

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 10000
#define N 100001
#define T 200001
#define M 1000000007
using namespace std;
typedef long long ll;
int f[N],g[N],p[K],t[N],mul[N],fac[T],rev[T],n,m,k,cnt,ans;
inline int read(){
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)){ret=(ret<<1)+(ret<<3)+c-'0';c=getchar();}
	return ret;
}
inline int po(int x,int k){
	int ret=1;
	while(k){
		if(k&1) ret=1ll*ret*x%M;
		x=1ll*x*x%M;k>>=1;
	}
	return ret;
}
inline ll C(int n,int m){
	return 1ll*fac[n]*rev[n-m]%M*rev[m]%M;
}
inline int chk(int k){
	int l=g[k],ret=0;
	while(k>1){
		if(k%l) return 0;
		k/=l;++ret;
	}
	return ret;
}
inline void prime(){
	for(int i=2;i<N;++i){
		if(!g[i]){
			g[i]=i,p[++cnt]=i;
		}
		for(int j=1,l,q,c;j<=cnt&&i*p[j]<N;++j){
			g[c=i*p[j]]=p[j];
			if(!(i%p[j])) break;
		}
		mul[i]=chk(i);
	}
}
inline void sieve(){
	cnt=0;t[1]=1;
	for(int i=1;i<=n;++i) g[i]=0;
	for(int i=2;i<=n;++i){
		if(!g[i]){
			g[i]=i,p[++cnt]=i,t[i]=k;
		}
		for(int j=1,l,q,c,x;j<=cnt&&i*p[j]<=n;++j){
			g[c=i*p[j]]=p[j];
			if(mul[c]) t[c]=C(mul[c]+k-1,k-1); 
			else{
				q=c;t[c]=1;
				while(q>1){
					x=l=g[q];
					while(true){
						if(1ll*x*l>1ll*q||q%(x*l)) break;
						x=x*l;
					}
					q/=x;
					t[c]=1ll*t[c]*t[x]%M; 
				}
			}
			if(!(i%p[j])) break;
		}
	}
}
inline void Aireen(){
	fac[0]=fac[1]=1;
	for(int i=2;i<T;++i)
		fac[i]=1ll*fac[i-1]*i%M;
	rev[T-1]=po(fac[T-1],M-2);
	rev[0]=rev[1]=1;
	for(int i=T-2;i>1;--i)
		rev[i]=1ll*rev[i+1]*(i+1)%M;
	prime();
	m=read();
	while(m--){
		n=read();k=read();
		for(int i=1;i<=n;++i) f[i]=read();
		sieve();
		for(int i=1;i<=n;++i) g[i]=0;
		for(int i=1;i<=n;++i)
			for(int j=i;j<=n;j+=i){
				g[j]+=1ll*t[j/i]*f[i]%M;
				if(g[j]>M) g[j]-=M;
			}
		for(int i=1;i<=n;++i)
			printf("%d ",g[i]);
		putchar('\n');
	}
}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	Aireen();
	fclose(stdin);
	fclose(stdout);
	return 0;
} 

2017-04-05 22:54:41

posted @ 2021-11-26 20:08  Aireen_Ye  阅读(28)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.