poj 2154 Color(欧拉函数+Polya)

题意:用n种颜色涂长度为n的项链,求不等价的方案数。

思路:题中等价只包含旋转的方案。对于旋转,可以知道循环节个数为gcd(i,n) (i=0 to n-1),由于n非常大,因此不可能直接枚举。我们可以枚举n的因子k,若gcd(i,n)=k,那么要满足gcd(i/k,n/k)=1,因此对于n的每个因子,求n/k的欧拉函数即可。剩下就是一些细节的处理了。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=40000+10;
int primes[maxn],pcnt,mod;
bool flag[maxn];
void getprimes()
{
    memset(flag,0,sizeof(flag));
    pcnt=0;
    for(int i=2;i<maxn;++i)
    {
        if(!flag[i])
        {
            primes[pcnt++]=i;
            for(ll j=(ll)i*i;j<maxn;j+=i)
                flag[j]=true;
        }
    }
}
int euler_phi(int x)
{
    int ans=x;
    for(int i=0;(ll)primes[i]*primes[i]<=x;++i)
    {
        if(x%primes[i]==0)
        {
            ans=ans-ans/primes[i];
            while(x%primes[i]==0) x/=primes[i];
        }
    }
    if(x>1) ans=ans-ans/x;
    return ans%mod;
}
ll pow_mod(ll x,ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1) res=res*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return res;
}
int factor[110],cnt[110],tot;
ll ans;
void dfs(int x,int now,int n)
{
    if(x==tot)
    {
        ans=ans+pow_mod(n,now-1)*euler_phi(n/now)%mod;
        ans%=mod;
        return ;
    }
    int tmp=1;
    for(int i=0;i<=cnt[x];++i)
    {
        dfs(x+1,now*tmp,n);
        tmp*=factor[x];
    }
}
void solve(int n)
{
    int N=n;
    tot=0;
    for(int i=0;(ll)primes[i]*primes[i]<=N;++i)
    {
        if(N%primes[i]==0)
        {
            factor[tot]=primes[i];
            cnt[tot]=0;
            while(N%primes[i]==0) {cnt[tot]++;N/=primes[i];}
            tot++;
        }
    }
    if(N>1) {factor[tot]=N;cnt[tot++]=1;}
    ans=0;
    dfs(0,1,n);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    getprimes();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d%d",&n,&mod);
        solve(n);
        printf("%lld\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值