polya +phi

poj 2154:


这题和2409 类似,不同之处在于,只考虑旋转,不考虑翻转;
因此相对前面题目应该说是更简单,
但一看数据范围,就不是这么回事了,2409完全可以直接循环处理,
但这题目n最大达100000000,显然会TLE,故需寻求更佳的解决方案。
用欧拉函数进行优化:


旋转:顺时针旋转i格的置换中,循环的个数为gcd(i,n),
每个循环的长度为n/gcd(i,n)。
如果枚举旋转的格数i,复杂度显然较高。有没有好方法呢?
可以不枚举i,反过来枚举L。
由于L|N,枚举了L,再计算有多少个i使得0<=i<=n-1并且L=gcd(i, n)。
即gcd(i,n)=n/L。
不妨设a=n/L=gcd(i, n),
不妨设i=a*t则当且仅当gcd(L,t)=1时
Gcd(i,n)=gcd(a*L,a*t)=a。
因为0<=i<n,所以0<=t<n/a=L.
所以满足这个条件的t的个数为Euler(L).
最后的结果为
phi(L)*N^(N/L-1)之和;
2154 Accepted 164K 1532MS C++ 1012B 
我的代码:朴素求欧拉函数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
int n,mod;
int phi(int n)
{
    int rea=n;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        rea=rea-rea/i;
        while(n%i==0)
        {
            n/=i;
        }
    }
    if(n>1)
    rea=rea-rea/n;
    return rea%mod;
}
int quickpow(int m,int n,int k)
{
    m=m%k;
    int b=1;
    while(n>0)
    {
        if(n&1)
        b=(b*m)%k;
        n=n>>1;
        m=(m*m)%k;
    }
    return b;
}
int main()
{
    int i,j,k,t,ans;
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        scanf("%d%d",&n,&mod);
        for(i=1;i*i<=n;i++)
        {
            if(i*i==n)
            ans=(ans+phi(i)*quickpow(n,i-1,mod))%mod;
            else
            if(n%i==0)
            {
              ans=(ans+phi(i)*quickpow(n,n/i-1,mod)+phi(n/i)*quickpow(n,i-1,mod))%mod;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
网上的代码:素数打表求欧拉函数
       Accepted 220K 1172MS C++ 1488B 
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 36000
int n, mod, ans;
int prim[35000];
bool flag[maxn + 20];


void get_prim() {
    memset(flag, 0, sizeof (flag));
    for (int i = 2; i <= 1000; i++)if (!flag[i])
            for (int j = i * i; j <= maxn; j += i)flag[j] = true;
    for (int i = 2, k = 0; i <= maxn; i++)
        if (!flag[i])prim[k++] = i;
}


int eular(int n) {
    int i = 0, ans = 1;
    for (i = 0; prim[i] * prim[i] <= n; i++) {
        if (n % prim[i] != 0)continue;
        ans *= prim[i] - 1;
        n /= prim[i];
        while (n % prim[i] == 0) {
            ans *= prim[i];
            n /= prim[i];
        }
    }
    if (n > 1)ans *= n - 1;
    return ans % mod;
}


int f(int c, int k, int mod) {
    int ans = 1;
    c = c % mod;
    while (k) {
        if (k & 1)ans = (c * ans) % mod;
        k >>= 1;
        c = (c * c) % mod;
    }
    return ans;
}


int main() {
    get_prim();
    int i, T;
    scanf("%d", &T);
    while (T-- && scanf("%d%d", &n, &mod)) {
        ans = 0;
        for (i = 1; i * i <= n; i++) {
            if (i * i == n)//枚举循环长度l,找出相应的i的个数:gcd(i,n)=n/l.
                ans = (ans + f(n, i - 1, mod) * eular(i)) % mod;
            else if (n % i == 0)//有长度为l的循环,就会有长度为n/l的循环。
                ans = (ans + f(n, n / i - 1, mod) * eular(i) + eular(n / i) * f(n, i - 1, mod)) % mod;
        }
        printf("%d\n", ans);
    }
}

你可能感兴趣的:(polya +phi)