Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 4655 | Accepted: 1563 |
Description
You only need to output the answer module a given number P.
Input
Output
Sample Input
5 1 30000 2 30000 3 30000 4 30000 5 30000
Sample Output
1 3 11 70 629
Source
此题是大规模的polya置换,小范围的进行联通分量的搜索已经无济于事。现在我们要最求更快速的方法,那么如何求得呢?这里有很多组合数学书上没有的结论,
第一就是:N个元素x位的平移置换的连通分量为:GCD(N,x),神奇吧!
第二就是重点了:就是快速求得有多首先,直接应用polya定理就可以得到ans=(N^gcd(0,N)+N^gcd(1,N)+…+N^gcd(N-1,N))/N,如果直接计算的话,即便应用快速幂,也改变不了循环计算N项和的复杂度。于是便不得不从gcd的值入手看是否能合并一些项达到化简的目的了。尽管一共有N项,但gcd的值一定到不了N项,因为gcd是N的约数,而一个数的约数是没有那么多的。顺着这个思路想的话,我们有没有办法更快地求得N的所有约数呢?这一点是可以在O(sqrt(N))的时间内办到的。我们只要枚举sqrt(N)以内的能够整除N的整数,就可以找到N所有的约数,如果同时我们可以O(1)的时间求出指数为该约数的项一共有多少的话,那么这个题整体就是O(sqrt(N))的复杂度了。那么这时就还剩一个问题没有解决了,对于N的任意一个约数,我们不妨设其值为d,指数为d的项一共有多少呢?也就是说一共有多少个满足gcd(x,N)=d的x呢?实际上,由于N和x的最大公约数是d,那么x/d和N/d必然互素,同时x<N,那么x的数量也就是N/d的欧拉函数值。最后还有一个事情,就是/N的问题,由于和式的每一项都是N^gcd,于是每项直接变成N^(gcd-1)即可。解决完上述几个问题之后,程序就不难写了,不过由于这个题时限卡得比较紧,所以求欧拉函数时要用素数表优化,同时筛素数的时候又可以把一些比较小的数的欧拉函数顺便也求出来。
其实也可不必找出所有的素数,先将N分解因子,然后通过枚举所有的约数(这个DFS一下即可),在DFS的过程中,也要注意现场还原,这么基本的东西也把我搞的debug了好长时间才发现,自己原来没有现场还原才导致WA好多次。
#include <iostream>
#include <cstdio>
using namespace std;
#define LL long long
const int MAX = 201;
int sizea[MAX];
LL a[MAX];
LL N, M, P, x;
LL res;
int size;
LL quick(LL base, LL p) {
if (p == 0) {
return 1 % P;
}
if (p & 1) {
return (base % P)* (quick(base, p - 1) % P) % P;
} else {
LL temp = quick(base, p / 2) % P;
return temp * temp % P;
}
}
LL cal_rula(LL num) {
for (int i = 0; i < size; i++) {
if (sizea[i] > 0) {
num /= a[i];
num *= (a[i] - 1);
}
}
return num;
}
/*枚举,居然忘了还原,写惨了!!!*/
void DFS(int depth, LL num) {
if (depth >= size) {
// cout << num << endl;
res = res + (cal_rula(N / num) % P) * quick(N, num - 1) % P;
res %= P;
return;
}
DFS(depth + 1, num);
/*比较之后*/
int temp = sizea[depth];
while (sizea[depth]) {//这种写法要注意,不能在里面减,虽然先判断,但还是再减的。
sizea[depth]--;
num *= a[depth];
DFS(depth + 1, num);
}
sizea[depth] = temp; //让人哭的错误,居然最基本的还原都忘了!!!
}
int main() {
int T;
while (scanf("%d", &T) != EOF) {
while (T--) {
scanf(" %llu", &N);
scanf(" %llu", &P);
x = N;
size = 0;
LL temp = N;
for (LL i = 2; i * i <= temp; i++) {
if (temp % i == 0) {
sizea[size] = 0;
a[size] = i;
while (temp % i == 0) {
temp /= i;
sizea[size]++;
}
size++;
}
}
if (temp > 1) {
sizea[size] = 1;
a[size] = temp;
size++;
}
res = 0;
DFS(0, 1);
printf("%llu\n", res % P);
}
}
return 0;
}