POJ2154-Color(Polya+欧拉函数优化)

9 篇文章 0 订阅
3 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值