Loading

P5221 Product 题解

P5221 Product

\[\prod_{i=1}^{n} \prod_{j=1}^{n} \dfrac{\operatorname{lcm}(i,j)}{\gcd(i,j)}\\=\prod_{i=1}^{n} \prod_{j=1}^{n} \dfrac{ij}{\gcd(i,j)^2}\\=\dfrac{\prod\limits_{i=1}^n i^{2n}}{\prod_\limits{i=1}^n\prod_\limits{j=1}^n gcd(i,j)^2} \]


\[\prod_{i=1}^n\prod_{j=1}^n gcd(i,j)\\=\prod_{d=1}^n d^{\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)==d]}\\ \]


\[\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)==d]\\=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}} [gcd(i,j)==1]\\=\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}\sum_{x|gcd(i,j)}\mu(x)\\=\sum_{x=1}^{\frac{n}{d}}\mu(x)(\dfrac{n}{dx})^2 \]

式子推过头了/kk,虽然这样也能做。

其实直接筛 \(\varphi\) 就好了啊

\[\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)==d]=\sum_{i=1}^{\frac{n}{d}}\varphi(i) \]

然后一层一层代回去就好了

const int N=1000005;
const int mod=104857601;
int n,fz=1,fm=1,ans;
int pri[N],cnt,phi[N];
bool vis[N];
int qpow(int n,int k){
	int res=1;
	while(k){
		if(k&1)res=1ll*res*n%mod;
		n=1ll*n*n%mod,k>>=1;
	}
	return res;
}
void qmod(int&x,int mod){
	x+=x>>31&mod,x-=mod,x+=x>>31&mod;
}
void Sieve(const int&N) {
	phi[1]=1;
	for (int i=2;i<=N;++i){
		if(!vis[i])pri[++cnt]=i,phi[i]=i-1;
		for (int j=1;j<=cnt&&i*pri[j]<=N;++j) {
			vis[i*pri[j]]=1;
			if (i%pri[j]==0){phi[i*pri[j]]=phi[i]*pri[j];break;}
			phi[i*pri[j]]=phi[i]*phi[pri[j]];
		}
	}
	for(int i=1;i<=N;++i)qmod(phi[i]+=phi[i-1],mod-1);
}
signed main(){
	n=read();
//	n=1000000;
	Sieve(n);
	for(int i=1;i<=n;++i)fz=1ll*fz*qpow(i,2*n)%mod;
	for(int i=1;i<=n;++i)fm=1ll*fm*qpow(qpow(i,phi[n/i]*2-1),mod-2)%mod;
	ans=1ll*fz*fm%mod*fm%mod,printf("%d\n",ans);
	return 0;
}

于是发现您只有 \(0\) 分,大片MLE

???

\(O(n)\) 的空间怎么MLE的

看一眼空间:\(7.81MB\) 怎么两位小数啊

开始算空间。

\(2e6\)\(int\)\(1e6\)\(bool\)\((2e6*4+1e6)/1024/1024=8.58306884765625MB\)

???????????????

该咋办啊

筛完发现质数只有 \(78498\) 个,开到 \(78500\) ,再算。

\((1078500*4+1000000)/1024/1024=5.0678253173828125\)

可以过了。

然后兴冲冲交一下,大概可以得到 \(70-80\) 不等的分数,其余全部TLE。

这个 \(\color{black}{\texttt{C}} \color{red}{\texttt{YJian}}\) 怎么这么毒瘤啊

然后问了 \(\color{black}{\texttt{c}} \color{red}{\texttt{yn2006}}\),他告诉我全是快速幂的问题,跑的太满了。

一开始还以为是线性筛的问题/kk。

实测:

线性筛:\(0.04s\)

快速幂:\(1.02s\)

接着开始优化:

\[\prod\limits_{i=1}^n i^{2n}=(n!)^{2n} \]

于是 \(n\) 次快速幂被优化成了 \(n\) 次乘法加一次快速幂,交了一发就过了。

但是 \(\color{black}{\texttt{c}} \color{red}{\texttt{yn2006}}\) 又告诉我可以优化一倍常数。

for(int i=1;i<=n;++i)fm=1ll*fm*qpow(qpow(i,phi[n/i]*2-1),mod-2)%mod;

可以变成

for(int i=1;i<=n;++i)fm=1ll*fm*qpow(i,(phi[n/i]<<1)-1)%mod;
fm=qpow(fm,mod-2);

少了 \(n\) 次快速幂。

最终本地 \(0.1s\) ,优化了 \(10\) 倍常数((

卡常太恐怖了!!!

给个大样例以供调试:

Input
1000000
Output
101067080

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x*f;
}
const int N=1000005;
const int mod=104857601;
int n,fz=1,fm=1,ans;
int pri[785050],cnt,phi[N];
bool vis[N];
inline int qpow(int x,int y,int ret=1){
	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1)
		ret=1ll*ret*x%mod; return ret;
}
void qmod(int&x,int mod){
	x+=x>>31&mod,x-=mod,x+=x>>31&mod;
}
void Sieve(const int&N) {
	phi[1]=1;
	for (int i=2;i<=N;++i){
		if(!vis[i])pri[++cnt]=i,phi[i]=i-1;
		for (int j=1;j<=cnt&&i*pri[j]<=N;++j) {
			vis[i*pri[j]]=1;
			if (i%pri[j]==0){phi[i*pri[j]]=phi[i]*pri[j];break;}
			phi[i*pri[j]]=phi[i]*phi[pri[j]];
		}
	}
	for(int i=1;i<=N;++i)qmod(phi[i]+=phi[i-1],mod-1);
}
signed main(){
	n=read(),Sieve(n);
	for(int i=1;i<=n;++i)fz=1ll*i*fz%mod;fz=qpow(fz,n<<1);
	for(int i=1;i<=n;++i)fm=1ll*fm*qpow(i,(phi[n/i]<<1)-1)%mod;
	fm=qpow(fm,mod-2);
	ans=1ll*fz*fm%mod*fm%mod,printf("%d\n",ans);
	return 0;
}
posted @ 2020-09-27 21:06  zzctommy  阅读(139)  评论(0编辑  收藏  举报