POJ2154-Color(Polya+歐拉函數優化)

Color
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 11077 Accepted: 3589
Description

Beads of N colors are connected together into a circular necklace of N beads (N<=1000000000). Your job is to calculate how many different kinds of the necklace can be produced. You should know that the necklace might not use up all the N colors, and the repetitions that are produced by rotation around the center of the circular necklace are all neglected.

You only need to output the answer module a given number P.
Input

The first line of the input is an integer X (X <= 3500) representing the number of test cases. The following X lines each contains two numbers N and P (1 <= N <= 1000000000, 1 <= P <= 30000), representing a test case.
Output

For each test case, output one line containing the answer.
Sample Input

5
1 30000
2 30000
3 30000
4 30000
5 30000
Sample Output

1
3
11
70
629
題目:這裏寫鏈接內容
題意:給定一個n,p。表示有n中顏色的珠子,串成一個n長度的手鍊,旋轉相同視爲同一種方案,輸出不重複的方案數,答案%p。
思路:題意很簡單,裸的Polya公式,按照以前的思路,只需要計算i=1~n時,n^(gcd(n,i)-1)即可,但是n<=1e9。光是遍歷一遍都會炸,所以我們要考慮優化一下這個算法。
gcd(i,n)一定是n的因子,n的因子肯定不會太多,意味着很多gcd(n,i)都是相同的,如果我們能求出gcd相同的i的個數,那麼就可以將算法優化到logn的複雜度
優化過程:
設i=g*x
n=g*y
滿足gcd(n,i)=g的條件顯然是x,y互質,那麼只要知道1~y中有多少個x與y互質即可。這裏顯然可以用歐拉函數優化
AC代碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<math.h>
#define met(s,k) memset(s,k,sizeof s)
#define scan(a) scanf("%d",&a)
#define scanl(a) scanf("%lld",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannl(a,b) scanf("%lld%lld",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define prin(a) printf("%d\n",a)
#define prinl(a) printf("%lld\n",a)
using namespace std;
typedef long long ll;
const int maxn=50000;
int isprime[maxn],prime[maxn],pcont,flen,factor[maxn],n,mod;
void init()//篩出素數便於求歐拉函數
{
    for(int i=2; i<maxn; i++)
    {
        if(!isprime[i])
        {
            prime[pcont++]=i;
            for(int j=2*i; j<maxn; j+=i)
                isprime[j]=1;
        }
    }
}
void Dqfactor(int x )//這裏我先對n進行了分解質因數,多此一舉,反而怎加了複雜度
{
    flen=0;
    for(int i=0; prime[i]*prime[i]<=x&&x!=1; i++)
    {
        if(x%prime[i]==0)
        {
            factor[flen++]=prime[i];
            while(x%prime[i]==0)x/=prime[i];
        }
    }
    if(x!=1)factor[flen++]=x;
}
int euler(int x)//這裏直接用素數去枚舉因子即可,我是直接用分解質因數的因子去除的
{
    int res=x;
    for(int i=0;i<flen&&x!=1;i++)
    {
        if(x%factor[i]==0)
        {
            res=res/factor[i]*(factor[i]-1);
            while(x%factor[i]==0)x/=factor[i];
        }
    }
    return res%mod;
}
ll ksm(ll x,ll y)//配合快速冪使用,效果更佳
{
    ll res=1;
    x%=mod;
    while(y)
    {
        if(y&1)res=res*x%mod;
        y/=2;
        x=x*x%mod;
    }
    return res;
}
int polya()
{
    int ans=0;
    for(int i=1; i*i<=n; i++)
    {
        if(n%i==0)
        {
            ans=(ans+euler(i)*ksm(n,n/i-1))%mod;
            if(n/i!=i)ans=(ans+euler(n/i)*ksm(n,i-1))%mod;//對於兩個因子都是i 的,顯然只要一個即可
        }
    }
    return ans;
}
int main()
{
    init();
    int t;
    scan(t);
    while(t--)
    {
        scann(n,mod);
        Dqfactor(n);
        prin(polya());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章