题目大意
给出多重数集 S = x 1 , x 2 , ⋯ , x n S={x_1,x_2,\cdots,x_n} S = x 1 , x 2 , ⋯ , x n ,求其所有大小为 k k k 的子集的 gcd \gcd g cd 之积。共 T T T 组数据,要求答案 m o d P \bmod P mod P 输出。
不保证 S S S 中所有数各不相同,不保证 P P P 为质数。T ≤ 60 , 1 0 6 ≤ P ≤ 1 0 14 , 1 ≤ x i ≤ 8 × 1 0 4 , 1 ≤ ∣ S ∣ ≤ 40000 , 1 ≤ k ≤ min ( ∣ S ∣ , 30 ) T≤60,10^6≤P≤10^{14},1≤x_i≤8\times 10^4,1≤|S|≤40000,1≤k≤\min(|S|,30) T ≤ 60 , 1 0 6 ≤ P ≤ 1 0 14 , 1 ≤ x i ≤ 8 × 1 0 4 , 1 ≤ ∣ S ∣ ≤ 40000 , 1 ≤ k ≤ min ( ∣ S ∣ , 30 ) 。
思路
枚举所有可能出现的 gcd \gcd g cd ,设 gcd = g \gcd=g g cd= g 的长度为 k k k 的子集个数为 c g c_g c g ,那么最终答案 $ans=\prod\limits_{g=1}^{\max\limits_{1\le i\le n}\{x_i\}}g^{c_g}$。我们的任务是求出每一个 c g c_g c g 。
枚举 g g g 时,统计 S S S 中 g g g 的倍数出现的个数 n u m g = ∑ i = 1 n [ g ∣ x i ] num_g=\sum\limits_{i=1}^n[g\mid x_i] n u m g = i = 1 ∑ n [ g ∣ x i ] ,从这 n u m g num_g n u m g 个中数字中选 k k k 个组成备选集合 S ′ S' S ′ ,共 C n u m g k C_{num_g}^k C n u m g k 中方案,S ′ S' S ′ 中所有数的 gcd \gcd g cd 必然是 g g g 的倍数 ,从 C n u m g k C_{num_g}^{k} C n u m g k 个方案中去掉 gcd = 2 g , 3 g ⋯ \gcd=2g,3g\cdots g cd= 2 g , 3 g ⋯ 的方案,剩下的方案数即为 c g c_g c g 。只要倒叙枚举 g g g ,c g = C n u m g k − c 2 g − c 3 g − ⋯ c_g=C_{num_g}^k-c_{2g}-c_{3g}-\cdots c g = C n u m g k − c 2 g − c 3 g − ⋯ 。
题目要求答案对 P P P 取模,考虑到 c g c_g c g 很大,我们要进行欧拉降幂。需要注意的是,当 gcd ( a , m ) = 1 \gcd(a,m)=1 g cd( a , m ) = 1 ,也可直接用下面式子的第三条。
$$
扩展欧拉定理\quad a^b \equiv \begin{cases} a^{b\ \bmod\ \varphi(m)}, &\gcd(a,m) = 1, \\ a^b, &\gcd(a,m)\ne 1, b < \varphi(m),\\ a^{b\ \bmod\ \varphi(m) +\varphi(m)}, &\gcd(a,m)\ne 1, b \ge \varphi(m). \end{cases} \pmod m
$$
$$
欧拉函数\quad\varphi(m)=m\times \prod\limits_{p_i\in \mathbb{prime},p_i\mid m}\left(1-\frac{1}{p_i}\right)
$$
考虑到 P P P 很大,我们用 Pollard-Rho 算法质因数分解得到 P P P 的所有质因子,再计算 φ ( P ) \varphi(P) φ ( P ) 。同时由于最后统计答案多次用到组合数,且组合数是作为指数出现,因此可以预处理组合数;因为 φ ( P ) φ (P) φ ( P ) 不一定为质数,同时 k k k 不大,所以我们可以用杨辉三角递推组合数。模乘法需要用龟速乘,但是有__int128 。
最终要枚举 g ∈ [ 1 , max { x i } ] g\in[1,\max\{x_i\}] g ∈ [ 1 , max { x i }] 计算答案,对当前于 g g g ,要枚举 c 2 g , c 3 g , ⋯ c_{2g},c_{3g},\cdots c 2 g , c 3 g , ⋯ 计算 c g c_g c g ,总的时间复杂度是 $O\left(\max\limits_{1\le i\le n}\{x_i\}\ln\left(\max\limits_{1\le i\le n}\{x_i\}\right)\right)$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 #include <iostream> #include <ctime> #include <algorithm> #include <cstring> #include <unordered_set> using namespace std;typedef long long ll;typedef __int128 bll;int _;unordered_set<ll>primeFactor; int n, k;ll P, phiP; ll combine[80004 ][32 ]; int cnt[80004 ];ll c[80004 ]; ll QuickPow (ll a, ll b, ll MOD) { a %= MOD; ll res = 1 % MOD; while (b) { if (b & 1 ) { res = (bll)res * (bll)a % MOD; } b >>= 1 ; a = (bll)a * (bll)a % MOD; } return res; } bool MillerRabin (ll n, int repeat) { if (n == 2 || n == 3 ) { return true ; } else if (n % 2 == 0 || n == 1 ) { return false ; } ll d = n - 1 ; int s = 0 ; while (!(d & 1 )) { ++s; d >>= 1 ; } while (repeat--) { ll a = rand () % (n - 3 ) + 2 ; ll x = QuickPow (a, d, n); ll y = 0 ; for (int i = 1 ;i <= s;i++) { y = (bll)x * (bll)x % n; if (y == 1 && x != 1 && x != n - 1 ) { return false ; } x = y; } if (y != 1 ) { return false ; } } return true ; } ll PollardRho (ll n, ll c) { ll x = rand () % (n - 2 ) + 1 ; ll y = x, i = 1 , k = 2 ; while (1 ) { ++i; x = (bll)x * (bll)x % n + c + n; ll d = abs (__gcd(y - x, n)); if (1 < d && d < n) { return d; } else if (y == x) { return n; } else if (i == k) { y = x; k <<= 1 ; } } } void FindPrimeFactor (ll n, ll c) { if (n == 1 ) { return ; } if (MillerRabin (n, 50 )) { primeFactor.emplace (n); return ; } ll p = n; while (p >= n) { p = PollardRho (p, c--); } FindPrimeFactor (p, c); FindPrimeFactor (n / p, c); } void init () { FindPrimeFactor (P, 103 ); phiP = P; for (const ll& p : primeFactor) { phiP = phiP / p * (p - 1 ); } for (int i = 1 ;i <= n;i++) { combine[i][0 ] = combine[i][i] = 1 ; for (int j = 1 ;j < i && j <= k;j++) { combine[i][j] = combine[i - 1 ][j] + combine[i - 1 ][j - 1 ]; if (combine[i][j] >= phiP) { combine[i][j] = combine[i][j] % phiP + phiP; } } } } int main () { srand (time (0 )); for (cin >> _;_;_--) { scanf ("%d %d %lld" , &n, &k, &P); init (); int maxX = 0 ; for (int i = 1 ;i <= n;i++) { int x; scanf ("%d" , &x); maxX = max (maxX, x); ++cnt[x]; } ll ans = 1 ; for (int g = maxX;g >= 1 ;g--) { int num = 0 ; for (int x = g;x <= maxX;x += g) { num += cnt[x]; } c[g] = combine[num][k]; for (int x = 2 * g;x <= maxX;x += g) { c[g] -= c[x]; } if (c[g] >= phiP) { c[g] = c[g] % phiP + phiP; } else if (c[g] < 0 ) { c[g] = (c[g] % phiP + phiP) % phiP; } ans = (bll)ans * (bll)QuickPow (g, c[g], P) % P; } printf ("%lld\n" , ans); memset (cnt, 0 , sizeof (int ) * (maxX + 1 )); memset (c, 0 , sizeof (ll) * (maxX + 1 )); primeFactor.clear (); } return 0 ; }