题意:给出m种不同重量的石子, 每一种石子的个数无穷多。从中选出N个石子,排成一个环,使得环上的石子重量的最大公约数为1. 旋转同构视为相同方案。
解法:问题转化为从m种数中找出n(循环节个数)个最大公约数为1的数字的排列数,于是想到找补集:满足最大公约数为2的充分必要条件是所选的数都能被2整除,满足最大公约数为3的充分必要条件是所选的数都能被3整除,满足最大公约数为6的方案被重复计算了,于是要容斥原理解决。
即对于m个数,msqrt(m)时间内统计这m个数中能被i整除的个数nn[i]然后对于n', 假设g[i] = cnt[i] ^n,那么g[i]就是长度为N'的,最大公约数为i倍数的方案数。容斥时注意剪枝。
题目中n可能于模数10007相同,此时不存在乘法逆元,因此不能通过乘法逆元除n,要将模数乘以n,然后在模意义下做二进制模乘以防溢出(貌似与Rho-Pollard有关,没仔细研究)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Main{
int p[] = new int[2300], nn[] = new int[20010];
void build() {
boolean vis[] = new boolean[20010];
int cnt = 0;
p[0] = 1;
for (int i = 2; i < 20010; i++)
if (!vis[i]) {
p[++cnt] = i;
int j = i;
while (j < 20010) {
vis[j] = true;
j += i;
}
}
}
StreamTokenizer in = new StreamTokenizer(new BufferedReader(
new InputStreamReader(System.in)));
int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
int prime[] = new int[10010], count[] = new int[10010], cnt;
void divide(int n) {
cnt = 0;// 质因子数目
int mx = (int) (Math.sqrt(n) + 3);
if (n < mx)
mx = n;
for (int i = 2; i < mx; i++) {
if (n == 1)
break;
if (n % i == 0) {
prime[++cnt] = i;
count[cnt] = 0;
while (n % i == 0) {
n /= i;
count[cnt]++;
}
}
}
if (n != 1) {
prime[++cnt] = n;
count[cnt] = 1;
}
}
long pow(long x, long p) {
long ans = 1;
while (p > 0) {
if ((p & 1) == 1)
ans = muti_mod(ans,x);
p >>= 1;
x = muti_mod(x,x);// x*x can be long even x is int
}
return ans;
}
long muti_mod(long a, long b)
{
long n=mod;
long exp = a % n, res = 0;
while (b>0)
{
if ((b & 1)==1)
{
res += exp;
if (res > n)
res -= n;
}
exp <<= 1;
if (exp > n)
exp -= n;
b >>= 1;
}
return res;
}
long get(int now, int sum, long k) {
long ans = pow(nn[sum], k);
for (int i = now + 1; p[i]*sum<=mx; i++)
if (nn[p[i]*sum] >0)
ans = (ans - get(i, sum * p[i], k) + mod)% mod;
return ans;
}
long ans, mx;
void dfs(int now, long value, long euler) {
if (now == cnt + 1) {// value是n的因子
long temp = get(0, 1, n / value);
ans = (ans + euler * temp) % mod;
return;
}
dfs(now + 1, value, euler);
long temp = prime[now];
for (int i = 1; i <= count[now]; i++) {
dfs(now + 1, value * temp, muti_mod(euler * (prime[now] - 1),
(temp / prime[now]) ));
temp = muti_mod(prime[now],temp);
}
}
long polya() {
ans = 0;
divide(n);
dfs(1, 1, 1);
ans/=n;
mod/=n;
ans%=mod;
ans+=mod;
ans%=mod;
return ans;
}
int m, n;
int mod ;
void run() throws IOException {
int cas = nextInt();
build();
while (cas-- > 0) {
m = nextInt();
n = nextInt();
mod= 10007*n;
Arrays.fill(nn, 0);
mx = -1;
for (int i = 0; i < m; i++) {
int x = nextInt();
if (x > mx)
mx = x;
int j;
for (j = 1; j * j < x; j++)
if (x % j == 0) {
nn[j]++;
nn[x / j]++;
}
if (j * j == x)
nn[j]++;
}
System.out.println(polya());
}
}
public static void main(String[] args) throws IOException {
new Main().run();
}
}