[テンプレート]問題の解決策のポリA定理
新しいアルゴリズム。。。
まず、我々は、トピックを分析します:
シクロ図N色、異なる性質を依頼するプログラムの数をNポイント
だから、明らかに、これはポリA定理(ナンセンス、タイトル名はそれをすべて言う)であります
さんは、「アクション」のこの質問を見てみましょう、それは1つの操作のみがあることは明らかである - 翻訳(...一部が回転を言うが、私は個人的に訳語として好みます)
我々は、交換を列挙するために始めることができます
まず、我々は、各翻訳単位の長さを列挙し、0〜(N-1)を変換、それぞれ、n個存在する翻訳の異なる性質が明らかである単位番目
すべての翻訳プログラムで見つけることができ、交換の一種であります
したがって、我々は、私が翻訳いまユニットステッピング、翻訳の長さを列挙することができ、その後、シーケンスはバック「シフト」ようになっている、我々は、K個のステップの最小値と仮定する。次いでkが満たさなければならない。私は、k個の%N == 0 * K = LCM(N、I)/ I = N / GCD(N、I)について解くことができるので、N / K = GCD(N、I)が存在します「サイクル」、したがって、ポリA定理によれば、現在の置換「固定点」は、Nの数(色数)^ GCD(N、I)は、簡略のために、我々は、[i]はsの塊を置きます。
最後に、バーンサイドの補題は、我々は$ \ FRAC {sum_ {i = 0} ^ {N-1} S [I]} {N} $%$ MOD $のプログラム番号を知ることができます
だから我々はコードを再生することができます:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
inline int ksm(int x,int y,int z){
int ans=1;
while(y){
if(y&1){
ans=(1LL*ans*x)%z;
}
x=(1LL*x*x)%z;
y>>=1;
}
return ans%z;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
int ans=0;
for(int i=0;i<n;++i){
ans+=ksm(n,__gcd(n,i),mod);
ans%=mod;
}
ans=(1LL*ans*ksm(n,mod-2,mod))%mod;//逆元
printf("%d\n",ans);
}
return 0;
}
その後、それがTです。。。qwq
だから、どのように我々は、このプログラムが行う最適化することができますか?
以下は、テキストです---------------- ------------------
私たちは、簡単に(N、I)と同じ私は、答えの寄与がn ^ GCD(N、i)は、その後、私たちは列挙することを望むかもしれないとして、(最初のnで割ったかどうか)と同じであるGCDを作るために、気づくことができますGCD(N、I)!
ダウンの$ O(\のSQRT {N})$の数(N、I)について列挙GCDのでn個の列挙に相当し、それの列挙一部の我々複雑なので、今、私たちは何をする必要がありますすぐにこの問題を計算してみてください。
N I、及びN個(N-1)の0〜GCDを求めるの数の所定数について、私があります
我々第一ケースI = 1、I = N(便宜上)が算出されます。
I = 1、φの合計(N)番目の条件を満たし、I = N-Aが場合(GCD(0、N)= N)
したがって、両方の答えとANS1へ=(φ(N)* N + N ^ N)%MOD
だから、私たちは数のどの残りを見てみましょう
我々が発見した、-iがn Nので、あるGCDの何のO-(N-1)の数を求め、iは1に等しくないので、問題は、と等価です
どのくらいの1-Nとn GCDを探していると、番号iです。
そして、我々は条件が私の数の倍数でなければならないことがわかります。したがって、問題は、と等価である:1-N / I、どのように多くの番号jを満たすGCD(N、jは* i)を= Iを列挙する
この質問に対する答えはφであることを1-nは/私はどのように多くの数jを満たすのGCD(N / I、J)= 1、それは明らかである見つけることと等価である(N / I)
(なぜそんなに変換ステップああ!クレイジーはINGの...)
そこで、我々は、より高速の(x)はφを求める必要があります
もともと、我々はO(n)の時間ですべてのφを事前に入手することができますが、問題がn <= 1E9、良い時間であれば、その後、おそらくカードあまりにもが、明らかにスペースを許可しません、そう、私たちはより良い方法を見つける必要があります。
その素因数の一つは、再帰的な解決策を見つけるために、私たちは、φが乗法関数である、知っているので、我々はその性質に応じてすることができます。。。
デュ画面を教えるために参照すると、私たちが最初にすることができますプリ小さいφアウト(私は1E7に前処理しました)
その後、我々は最初の使用Miller_rabinアルゴリズムxは素数であり、もしそうであれば、我々はx 1に戻り、そうでなければ、我々は、x、再帰溶液を分解するpollard_rhoアルゴリズムを使用できるかどうかを決定し、マップの中央集合を促進することができます。問題が解決策を持っています。
もちろん、私は(初めから、我々はi = 1から同じ時間をカウントし、I = nの答え、それから1へ、より多くの計算はa)の文の答えの下で、特に覚えているのn = 1
セミ擬似コード:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7,N=1e7+1;
map<int,int>f;
int T,fi[N],zhi[N>>1],e;
inline int ksm(int x,int y,int z){
int ans=1;
while(y){
if(y&1){
ans=(1LL*ans*x)%z;
}
x=(1LL*x*x)%z;
y>>=1;
}
return ans;
}
inline void sai(int maxe){
T=maxe;fi[1]=1;
for(int i=2;i<=maxe;++i){
if(!fi[i]){
fi[i]=i-1;zhi[++e]=i;
}
for(int j=1;j<=e;++j){
if(i*zhi[j]>maxe){
break;
}
if(i%zhi[j]==0){
fi[i*zhi[j]]=fi[i]*zhi[j];
break;
}
fi[i*zhi[j]]=fi[i]*fi[zhi[j]];
}
}
}
inline bool Miller_rabin(int x){
//偷懒省略
}
inline int pollard_rho(int x){
//偷懒省略
}
inline int calc(int x){
if(x<=T){
return fi[x];
}
if(f.find(x)!=f.end()){
return f[x];
}
int ret=pollard_rho(x);
if(ret==x){
return x-1;
}
return f[x]=((x/ret)%ret==0?ret*calc(x/ret):(ret-1)*calc(x/ret));
}
int main(){
srand(1);
sai(1e7);//预处理
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
if(n==1){
puts("1");
continue;
}
int ans=(ksm(n,n,mod)+(1LL*n*calc(n))%mod)%mod;
for(int i=sqrt(n);i>=2;--i){//一次平移的距离
if(n%i==0){
ans+=(1LL*ksm(n,i,mod)*calc(n/i))%mod;
ans%=mod;
if(n/i!=i){
ans+=(1LL*ksm(n,n/i,mod)*calc(i))%mod;
ans%=mod;
}
}
}
ans=(1LL*ans*ksm(n,mod-2,mod))%mod;//逆元
printf("%d\n",ans);
}
return 0;
}