1. 程式人生 > 其它 >題解 poj 2154 Color

題解 poj 2154 Color

傳送門


【題意】

\(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;
}