波利亞定理 --- poj : 2154 Color
Time Limit: 2000MS | Memory Limit: 65536K |
Total Submissions: 7873 | Accepted: 2565 |
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
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
Source
Mean:
給你一個包含N個珠子的項鍊,現在有N種顏色,讓你從這N種顏色中選擇一些顏色來將這些珠子染色,問可以染出多少種不同的珠子。
analyse:
對於n比較小的情況我們可以直接暴力列舉置換群並統計其對應的C(f),考慮旋轉i個珠子的迴圈群,我們可以證明其對應的不動的染色方案C(fi)=n^gcd(i,n)。為什麼呢?我們可以這樣考慮,假設珠子編號為0~n-1,對於旋轉i個珠子的迴圈群,由於相鄰間的珠子對應的旋轉後位置還是相鄰,所以這個迴圈群的環必然是大小相等的。假設其環的大小為T,那麼就有(T*i)%n=0。假設T*i=k*n,i=g*x,n=g*y,其中g=gcd(i,n)。那麼T=k*y/x,因為k為整數,x、y互質,所以使得T最小且為正整數的k=x,那麼T=y,整個迴圈群中環的個數=n/T=g。所以我們可以得到C(fi)=n^gcd(i,n)。那麼這個題目就轉化為求sum{n^gcd(i,n)},1<=i<=n。
但是n<=10^9,這個資料範圍太大,直接列舉必然超時,我們要考慮優化。觀察上面的公式,我們發現雖然i的範圍很大,但是gcd(i,n)的值卻不多,最多為n的因子的個數。如果我們可以很快求出gcd(i,n)=g時i的個數,那麼我們就能夠得到一個很高效的演算法。假設i=g*x,n=g*y,gcd(i,n)=g的條件為x、y互質,又因為1<=x<=y。所以滿足條件的x的個數就是[1,y]裡和y互質的數的個數,這就等於phi(y)。尤拉函式的值可以在O(n^(1/2))的複雜度內算出來,於是我們就得到了一個高效的演算法。
Time complexity:O(n^(/12))
Source code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n, yu;
const int NN=10000000;
bool v[NN];
int p[NN];
int len=-1;
void make_p()
{
int i,j;
for(i=2; i<NN; ++i)
{
if(!v[i]) p[++len] = i;
for(j=0; j<=len && i*p[j] < NN; ++j)
{
v[i*p[j]] =1;
if(i%p[j] == 0) break;
}
}
}
int work(int n) {
int temp = n;
for (int i = 0; i < len && p[i]*p[i] <= temp; ++i)
{
if (temp % p[i] == 0) {
n -= n/p[i];
do {
temp /= p[i];
}while (temp % p[i] == 0);
}
}
if (temp != 1) {
n -= n/temp;
}
return n%yu;
}
int solve(int m) {
int ans = 1;
int s = n%yu;
int temp = m;
while (temp > 0) {
if (temp&1) {
ans = (ans * s) % yu;
}
s = (s*s)%yu;
temp >>= 1;
}
return ans;
}
int main() {
make_p();
int t;
scanf("%d", &t);
while (t--) {
scanf("%d %d", &n, &yu);
int res = 0;
for (int i = 1; i*i <= n; ++i) {
if (i*i == n)
{
res = (res + work(i)*solve(i-1))%yu;
}
else if (n%i == 0)
{
res = (res + work(i)*solve(n/i-1) + work(n/i)*solve(i-1))%yu;
}
}
printf("%d\n", res);
}
return 0;
}