码迷,mamicode.com
首页 > 其他好文 > 详细

POJ2154 Color【Polya定理】【欧拉函数】【整数快速幂】

时间:2015-08-20 18:56:06      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:

题目链接:

http://poj.org/problem?id=2154


题目大意:

给定 N 种颜色的珠子,每种颜色珠子的个数均不限,将这些珠子做成长度为 N 的项链。

问能做成多少种不重复的项链,最后结果对 P 取模。并且两条项链相同,当且仅当两条

项链通过旋转后能重合在一起,且对应珠子的颜色相同。


解题思路:

Polya定理的应用。先来看Polya定理。
Polya定理:设 G = {a1,a2,…,ag}是 N 个对象的置换群,用 M 种颜色给这 N 个
对象着色,则不同的着色 方案数为:
                  |G|^(-1) * {M^c(a1) + M^c(a2) + … + M^c(ag)}。
其中 c(ai)为置换 ai 的循环节数,( i = 1,2,…,g )。

对于这道题,直接用Polya定理求解,找出所有的置换,并求出置换的循环节数。然后

根据上边公式求出 M^c(ai) 的总和,再除以置换群个数。

旋转置换:分别顺时针旋转 i 个珠子,其循环节长度为 LCM(N,i) / i,循环节数为

N / (LCM(N,i) / i),即 GCD(N,i)。

方案数为:  N^(-1) * Σ{ N^GCD(N,i) } % P (0 <= i <= N)。

这里的N非常大,按照上边的方法遍历 i 从0 到 N,复杂度太高。

在枚举 i 的时候,会出现很多相同的GCD(N,i) == k,问题变为存在多少个 i,从而使

GCD(N,i) == k。

求GCD(N,i) == k的个数,其实就是求GCD(N/k,i/k) == 1的个数,答案为phi(N/k)

个。

因为k 为N的约数,则遍历 k 时,只需要从 2 到 sqrt(N) 即可。

方案数为: Σ{ phi(N/k) * N^(k-1) + phi(k) * N^(N/(k-1)) } % P (2 <= k <= sqrt(N))。

注意当 k == sqrt(N) 的时候,只加前部分,不要重复加,详细参考代码。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

bool IsPrime[50050];
int Prime[8010],Num;

void GetPrime()
{
    Num = 0;
    for(int i = 2; i <= 50000; ++i)
    {
        if(!IsPrime[i])
        {
            Prime[Num++] = i;
            for(int j = 1; j*i <= 50000; ++j)
            {
                IsPrime[i*j] = true;
            }
        }
    }
}

int Euler(int x)
{
    int ans = x;
    for(int i = 0; i < Num && Prime[i]*Prime[i] <= x; ++i)
    {
        if(x % Prime[i] == 0)
        {
            ans = ans / Prime[i] * (Prime[i]-1);
            while(x % Prime[i] == 0)
                x /= Prime[i];

        }
    }
    if(x > 1)
        ans = ans / x * (x-1);
    return ans;
}

int QuickMod(int a,int b,int m)
{
    int ans = 1 % m;
    a %= m;
    while(b > 0)
    {
        if(b & 1)
            ans = ans * a % m;
        a = a * a % m;
        b >>= 1;
    }
    return ans;
}

int main()
{
    int T,N,P;
    GetPrime();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&N,&P);
        int ans = 0;
        for(int i = 1; i*i <= N; ++i)
        {
            if(i*i == N)
                ans = (ans + Euler(i)%P *QuickMod(N,i-1,P)) %P; //不要重复加
            else if(N % i == 0)
                ans = ( ans + Euler(i)%P * QuickMod(N,N/i-1,P) + Euler(N/i)%P * QuickMod(N,i-1,P) ) % P;
        }

        printf("%d\n",ans);
    }

    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

POJ2154 Color【Polya定理】【欧拉函数】【整数快速幂】

标签:

原文地址:http://blog.csdn.net/lianai911/article/details/47811445

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!