祝います!第二トラック数論紫のテーマ。
プッシュ式は楽しかったです!
\ [\ prod_ {iは1 =} ^ N \ prod_ {J = 1} ^ N \ FRAC {\ operatorname {LCM}(i、j)は} {\ GCD(I、J)} \]
\ [= \ prod_ ^ {I 1 =} N \ prod_ {J = 1} ^ N \ FRAC {I \回J} {(\ GCD(I、J))^ 2} \]
\ [=(\ prod_ {I = 1} ^ N \ prod_ {J = 1} ^ NI \回J)\回(\ prod_が{iは1 =} ^ N \ prod_ {J = 1} ^ N \ FRAC { 1} {(\ GCD(I、J))^ 2})\]
式の前を見てください:
\ [=(\ prod_ {iは1 =} ^ N \ prod_ {J = 1} ^ NI \回J)\]
\ [=(\ prod_ {I = 1} ^ NI)^ {N 2 \回} \]
\ [=(N!)^ {2 \回N} \]
式の裏を見て:(脇に置く- \(2 \) )のパワー、そして最後に言って
\ [= \ prod_ {D = 1} ^ N \ prod_ {I = 1} ^ N \ prod_ {J = 1} ^ N [\ GCD(i、j)は== D] \]
\ [= \ prod_ {D = 1} ^ ND ^ {\ sum_ {i = 1} ^ {\ lfloor \ FRAC {n}は{D} \ rfloor} \ sum_ {J = 1} ^ {\ lfloor \ FRAC { n}は{D} \ rfloor} [\ GCD(i、j)は== 1]} \]
インデックスを見てください:
\ [\ sum_ {i = 1} ^ {\ lfloor \ FRAC {n}は{D} \ rfloor} \ sum_ {J = 1} ^ {\ lfloor \ FRAC {n}は{D} \ rfloor} [\ GCD( i、j)は== 1] \]
\ [= 2 \回(\ sum_ {i = 1} ^ {\ lfloor \ FRAC {n}は{D} \ rfloor} \ phi_i) - 1 \]
(このステップは、基準に基づいている羅区GCD P2568)
明らかにこの事、オイラーふるいおよびプレフィックスと処理することができます!
\(のf_i \)を表す\(phi_i \)プレフィックスと。
だから、最終的な答えは次のとおりです。
\ [(\ prod_ {I = 1} ^ N \ prod_ {J = 1} ^ NI \回J)\回(\ prod_が{iは1 =} ^ N \ prod_ {J = 1} ^ N \ FRAC {1 } {(\ GCD(I、J))^ 2})\]
\ [=(N!)^ {2 \回N} \倍\ prod_ {D = 1} ^ ND ^ {(2 \回_ {\ lfloor \ FRAC {n}は{D} \ rfloor} F - 1)^ {-2}} \]
この方法では、使用することができます\(Oを(N \ Nログ )\) 問題を解決します。
しかし、あなたはその質問のヘッド時間の制約がありますし、スペースの制約は、ビットタイトですが、かなり大きな定数は、その最適化を検討する必要がありました。
さらに、\(F \)アレイバースト(texttt {長い長い} \ \ \) 次に、問題であるだけでなく、弾性率と思われます。
注:と\(\ログ\)我々はすぐに計算パワーを必要とするためです。
我々が見つかりました(104 857 601 \)は\素数です。そして、それが素数で直接使用することができる品質の弾性率が出て死にます。
すなわち、オイラーの定理:
\ [^ {\ phi_p} \当量1 \ PMOD P \]
だから、再びのステップ:
\ [=(!N)^ {2 \回N} \倍\ prod_ {D = 1} _ {\ lfloor \ FRAC {n}は{D} \ rfloor} F ^ ND ^ {((2 \回 - 1) \%(MOD-1))^ { - 2}} \]
そして、直接の波オイラー画面の問題を解決します!
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=104857601;
const int N=1e6+1;
const int N1=1e5+1;
inline ll read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int n,phi[N],prime[N1],f=0;
bool h[N]; ll k=1;
inline ll pw(ll x,ll y) {
ll ans=1; while(y) {
if(y&1) ans=(ans*x)%MOD;
x=(x*x)%MOD; y>>=1;
} return ans;
} //快速幂模板
inline void Euler() {
phi[1]=1; h[1]=1;
for(int i=2;i<=n;i++) {
k=(ll(k*i)%MOD); //顺便处理个阶乘
if(!h[i]) prime[++f]=i,phi[i]=i-1;
for(int j=1;j<=f && i*prime[j]<=n;j++) {
h[i*prime[j]]=1;
if(i%prime[j]==0) {
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
} //欧拉筛模板
int main(){
n=read(); Euler();
// printf("%lld\n",k);
k=pw(k,n<<1); ll ans=1; //第一部分的值
// printf("%lld\n",k);
// for(int i=1;i<=n;i++) printf("%d ",phi[i]);
// putchar('\n');
for(int i=1;i<=n;i++) phi[i]=(phi[i]<<1)+phi[i-1]%(MOD-1); //计算2倍的时候直接模掉
for(int i=2;i<=n;i++) ans=(ans*pw(i,phi[n/i]-1))%MOD; //第二部分
printf("%lld\n",(k*pw(ll(ans*ans)%MOD,MOD-2))%MOD); //然后 -2 次方,就是2次方的倒数,而倒数已经转换为逆元
return 0;
}