題解 poj 2154 Color
阿新 • • 發佈:2021-07-10
【題意】
\(x\) 組資料
每組詢問 \(n\) 個顏色填塗 \(n\) 個空位的環,問不同構方案數,答案對 \(p\) 取模。
規定某個環能被另一個環旋轉得到時,這兩個環同構。
\(x\leq 3500, n\leq 10^9\)
【分析】
顯然 \(n\) 個角度的旋轉構成一個群,根據 polya 定理,得到答案為:
\(\displaystyle {1\over n}\sum_{i=1}^n n^{\gcd(i, n)}={1\over n}\sum_{d\mid n} n^d \boldsymbol \varphi({n\over d})=\sum_{d\mid n} n^{d-1} \boldsymbol \varphi({n\over d})\)
但如果暴力計算每一個 \(\boldsymbol \varphi({n\over d})\) 複雜度為 \(O(n^{3\over 4})\) 可能不夠,考慮先打出前 \({n^{2\over 3}}\) 個尤拉函式值,則求解所有尤拉函式的複雜度降低為 \(o(n^{2\over 3})\)(證明如下)
故求解一組的複雜度為 \(o(n^{2\over 3})+O(\sqrt n)+O(\sqrt n\log n)=o(n^{2\over 3})\),總複雜度即為 \(o(x\cdot n^{2\over 3})\),卡卡常能過
【程式碼】
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll, ll> pii; typedef double db; #define fi first #define se second int n, mod; inline int fpow(int a,int x) { ll ans=1; for(;x;x>>=1,a=(ll)a*a%mod) if(x&1) ans=(ll)ans*a%mod; return ans; } const int Lim=2e6, MAXN=Lim+10; int fc[MAXN], phi[MAXN], prime[MAXN], cntprime; inline int getphi(int n) { if(n<=Lim) return phi[n]; int val=n; for(int j=1;j<=cntprime;++j) if((ll)prime[j]*prime[j]>n) break; else if(n%prime[j]==0){ val=val/prime[j]*(prime[j]-1); while(n%prime[j]==0) n/=prime[j]; } if(n!=1) val=val/n*(n-1); return val; } inline int ans(){ int res=0; for(int i=1;i*i<=n;++i) if(n%i==0) { int j=n/i; res+=(ll)getphi(j)*fpow(n, i-1)%mod; res-=(res>=mod?mod:0); if(i!=j){ res+=(ll)getphi(i)*fpow(n, j-1)%mod; res-=(res>=mod?mod:0); } } return res; } inline void sieve(){ phi[1]=1; for(int i=2;i<=Lim;++i){ if(!fc[i]) fc[i]=prime[++cntprime]=i, phi[i]=i-1; for(int j=1;j<=cntprime;++j) if(prime[j]*i>Lim||prime[j]>fc[i]) break; else fc[prime[j]*i]=prime[j], phi[prime[j]*i]=phi[i]*(prime[j]-(prime[j]!=fc[i])); } } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); sieve(); int x; cin>>x; while(x--&&cin>>n>>mod) cout<<ans()<<"\n"; cout.flush(); return 0; }