poj 2154 Color

Color

题意:有N种颜色,项链上有N颗珠子;(1 <= N <= 1e^9)每种颜色的珠子有无限个;问通过旋转操作有多少种不同的项链;

思路:等价类计数问题 + 欧拉函数优化;

等价类计数:

旋转:旋转i颗珠子的间距;0,i,2i...构成一个循环;这个循环有n/gcd(n,i)颗珠子,总共有gcd(n,i)个循环;所以不动点总数的平均值就为 1/N*Σ(1 <= i <= n)  Ngcd(i,n)

优化:朴素算法的时间复杂度是O(n*log(n)),gcd的时间复杂度要小于log(n);实际上我们是枚举了i,通过gcd来求出循环节的个数的;易知循环节的个数d是n的约数(不只是素数),这可以在sqrt(n)内求解;如果我们通过枚举d来求出有多少k满足gcd(k,n) = d (欧拉函数)

gcd(k,n) = d,gcd(k/d,n/d) = 1,(1 <= k <= n) ==>gcd(k,n/d) = 1(1 <= k <= n/d);即 num(k) = Φ(n/d);这样公式就转化为Σd|n[Φ(N/d)*N(d-1)]

那时间复杂度就是sqrt(n)*log(n)*(约数个数);

注:这题在循环终止上要特别注意,否则容易TLE,卡常数。这道题将近1s才过..前几次在phi中卡了常数TLE了;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<time.h>
#include<stack>
#include<set>
#include<map>
#include<queue>
using namespace std;
#define rep0(i,l,r) for(int i = (l);i < (r);i++)
#define rep1(i,l,r) for(int i = (l);i <= (r);i++)
#define rep_0(i,r,l) for(int i = (r);i > (l);i--)
#define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
#define MS0(a) memset(a,0,sizeof(a))
#define MS1(a) memset(a,-1,sizeof(a))
#define MSi(a) memset(a,0x3f,sizeof(a))
#define inf 0x3f3f3f3f
#define lson l, m, rt << 1
#define rson m+1, r, rt << 1|1
typedef __int64 ll;
template<typename T>
void read1(T &m)
{
    T x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    m = x*f;
}
template<typename T>
void read2(T &a,T &b){read1(a);read1(b);}
template<typename T>
void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
template<typename T>
void out(T a)
{
    if(a>9) out(a/10);
    putchar(a%10+'0');
}
const int MAXN = 5e4;
int prime[MAXN],check[MAXN];
void getprime()
{
    int i,j;
    for(i = 2;i < MAXN;i++){
        if(check[i] == 0) prime[++prime[0]] = i;
        for(j = 1;j <= prime[0] && prime[j] < MAXN/i;j++){
            check[prime[j]*i] = 1;
            if(i%prime[j] == 0) break;
        }
    }
}
int N,p;
int phi(int d)
{
    int ans = d,tmp = d;
    for(int i = 1;prime[i]*prime[i] <= tmp;i++){//i <= prime[0] && prime[i] <= tmp 卡常数
        if(tmp%prime[i] == 0)
            ans -= ans/prime[i];
        while(tmp % prime[i] == 0) tmp /= prime[i];
    }
    if(tmp != 1) ans -= ans/tmp;
    return ans%p;
}
int pow_mod(int a,int n)
{
    if(n == 0) return 1;
    if(n == 1) return a;
    int ans = pow_mod(a,n/2);
    (ans = ans*ans) %= p;
    if(n & 1) ans = ans*a%p;
    return ans;
}
int main()
{
    int X;
    read1(X);
    getprime();
    while(X--){
        read2(N,p);
        int ans = 0, tmp = sqrt(N)+0.5;
        for(int i = 1;i <= tmp;i++){ //只需整除即可,未必是素数;
            if(N % i) continue;
            (ans += phi(N/i)*pow_mod(N%p,i-1)) %= p;
            if(N != i*i)
                (ans += phi(i)*pow_mod(N%p,N/i-1)) %= p;
        }
        out(ans);
        puts("");
    }
    return 0;
}
View Code

 

posted @ 2016-02-28 15:42  hxer  阅读(341)  评论(0编辑  收藏  举报