题目:https://ac.nowcoder.com/acm/contest/11253/J
大意为给一个数组a,求数组a中所有大小为k的子集的gcd的乘积。

根据惯例,估计是转化为枚举gcd后统计贡献。对于每个i,我们需要求有几个子集的gcd为i,考虑组合数为,num为数组中i的倍数的个数,但这样统计会包含gcd为i的倍数的集合,因此,考虑从大到小枚举,枚举一个i后,减去gcd为i的倍数的集合的个数,即可得到gcd为i的集合的个数。
综上所述,假设gcd为i的集合的个数为,则答案为
但是,还没完,因为数字有这么大,可想而知的值一定不小,因此,考虑欧拉降幂。我们可以先通过扩展欧拉定理求出(或者是)。由于为一堆组合数的累加,因此,考虑递推预处理组合数(不能用逆元,因为p不一定是质数)。
那么,对于高达1e14的p求,自然是要分解质因数后容斥的。
所以良心出题人为什么不直接开1e18,这样大家就都用pr了
题解说常熟小的暴力可以卡过去,但我是不行,因此,分解质因数用pr解决。
P.S. 好像很多同学关心加不加phi的问题,,我发现欧拉降幂挺多题其实都没故意卡这点。。偷懒点就不写了吧

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(int i = a; i <= n; ++ i)
#define per(i, a, n) for(int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}

namespace FastIO {
    const int SIZE = 1 << 16;
    char buf[SIZE], str[64];
    int l = SIZE, r = SIZE;
    int read(char *s) {
        while (r) {
            for (; l < r && buf[l] <= ' '; l++);
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        int cur = 0;
        while (r) {
            for (; l < r && buf[l] > ' '; l++) s[cur++] = buf[l];
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        s[cur] = '\0';
        return cur;
    }
    template<typename type>
    bool read(type &x, int len = 0, int cur = 0, bool flag = false) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        if (flag) x = -x;
        return true;
    }
    template <typename type>
    type read(int len = 0, int cur = 0, bool flag = false, type x = 0) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        return flag ? -x : x;
    }
} using FastIO::read;
const int maxn=4e4+10;
const int maxai=8e4+10;
const int maxlen=1e7+10;
int t,n,k;
ll p;
int a[maxn];
int cnt[maxai];
ll dp[maxai];

inline ll mul(ll a,ll b,ll mod){
        return ((a * b - (long long)((long long)((long double)a / mod * b + 1e-3) * mod)) % mod + mod) % mod;
}

inline ll ksm(ll a,ll n,ll mod){
    ll ans=1;
    assert(n>=0);
    while(n){
        if(n&1) ans=mul(ans,a,mod);
        a=mul(a,a,mod);
        n>>=1;
    }
    return ans%mod;
}
namespace PHR{
    typedef long long LL;
    ll n, Max = 0;
    inline ll quickpow(ll a, ll b, ll mod)
    {
        ll ret = 1;
        for(; b; b >>= 1, a = mul(a, a, mod))
            if(b & 1) ret = mul(ret, a, mod);
        return ret;
    }

    inline bool test(ll a, ll d, ll n)
    {
        ll t = quickpow(a, d, n);
        if(t == 1) return 1;
        while(d != n - 1 && t != n - 1 && t != 1) t = mul(t, t, n), d <<= 1;
        return t == n - 1;                       //这里就不用判1了,因为只可能在while前出现1的情况
    }
    int a[] = {2, 3, 5, 7, 11};
    inline bool miller_rabin(ll n)
    {
        if(n == 1) return 0;
        for(int i = 0; i < 5; ++i)        //先粗筛一遍
        {
            if(n == a[i]) return 1;
            if(!(n % a[i])) return 0;
        }
        ll d = n - 1;
        while(!(d & 1)) d >>= 1;
        for(int i = 1; i <= 5; ++i)        //搞五遍
        {
            ll a = rand() % (n - 2) + 2;//x属于[2, n - 1]
            if(!test(a, d, n)) return 0;
        }
        return 1;
    }

    inline ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    inline ll f(ll x, ll a, ll mod) {return (mul(x, x, mod) + a) % mod;}
    const int M = (1 << 7) - 1;            //我也不知道为啥M是这个数……
    ll pollard_rho(ll n)                    //倍增版!减少gcd调用次数。(好像不用判环?)
    {
        for(int i = 0; i < 5; ++i) if(n % a[i] == 0) return a[i];
        ll x = rand(), y = x, t = 1, a = rand() % (n - 2) + 2;
        for(int k = 2;; k <<= 1, y = x)
        {
            ll q = 1;
            for(int i = 1; i <= k; ++i)
            {
                x = f(x, a, n);
                q = mul(q, abs(x - y), n);
    //            if(i >= M)    //不等价!
                if(!(i & M))    //超过了2 ^ 7,再用gcd
                {
                    t = gcd(q, n);
                    if(t > 1) break;    //找到了
                }
            }
            if(t > 1 || (t = gcd(q, n)) > 1) break;    //之所以再求一遍,是处理刚开始k < M的情况
        }
        return t;
    }
    LL factor[100],total=0;
    inline void find(ll x)
    {
        if(x == 1 || x <= Max) return;
        if(miller_rabin(x)) {factor[++total]=x; return;}
        ll p = x;
        while(p == x) p = pollard_rho(x);
        while(x % p == 0) x /= p;
        find(p); find(x);
    }
}

ll x;

ll c[maxai][32];
ll PHIP;
inline ll C(ll n,ll k){
    if(n<k)return 0;
    return c[n][k];
}

inline ll phi(ll x){
    ll ans=x;
    for(ll i=2;1ll*i*i<=x;++i){
        if(x%i==0){
            ans=ans/i*(i-1);
            while(x%i==0)x/=i;
        }
    }
    if(x>1)ans=ans/x*(x-1);
    return ans;
}

signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    //clock_t c1 = clock();
    //===========================================================
    read(t);
    //init();
    while(t--){
        read(n),read(k),read(p);
        PHR::total=0;
        PHR::find(p);
        PHIP=p;
        for(int i=1;i<=PHR::total;++i)PHIP=PHIP/PHR::factor[i]*(PHR::factor[i]-1);
        c[0][0]=1;
        int mx=0;
        for(int i=1;i<=n;++i){
            int x;read(x);
            ++cnt[x];
            mx=max(mx,x);
        }
        for(int i=1;i<=n;++i){
            c[i][0]=1;;
            if(i<32)c[i][i]=1;
            for(int j=1;j<i&&j<=31;++j){
                c[i][j]=(c[i-1][j]+c[i-1][j-1]);
                if(c[i][j]>=PHIP)c[i][j]-=PHIP;
            }
        }
        ll ans=1;
        for(int i=mx;i>=1;--i){
            int now=0;
            for(int j=i;j<=mx;j+=i){
                now+=cnt[j];
            }
            dp[i]=C(now,k);
            for(int j=i+i;j<=mx;j+=i){
                dp[i]-=dp[j];
            }
            dp[i]=(dp[i]%PHIP+PHIP)%PHIP;
            ans=mul(ans,ksm(i,dp[i],p),p);
        }
        for(int i=0;i<=mx;++i){
            cnt[i]=dp[i]=0;
        }
        printf("%lld\n",ans);
    }
    //===========================================================
    //std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
    return 0;
}

水群的时候看到有聚聚给了个hack样例,扩展欧拉果然挂了,这里再补个修改后的代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
#define rep(i, a, n) for(int i = a; i <= n; ++ i)
#define per(i, a, n) for(int i = n; i >= a; -- i)
//#define ONLINE_JUDGE
using namespace std;
typedef long long ll;
const int mod=1e9+7;
template<typename T>void write(T x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        write(x/10);
    }
    putchar(x%10+'0');
}

namespace FastIO {
    const int SIZE = 1 << 16;
    char buf[SIZE], str[64];
    int l = SIZE, r = SIZE;
    int read(char *s) {
        while (r) {
            for (; l < r && buf[l] <= ' '; l++);
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        int cur = 0;
        while (r) {
            for (; l < r && buf[l] > ' '; l++) s[cur++] = buf[l];
            if (l < r) break;
            l = 0, r = int(fread(buf, 1, SIZE, stdin));
        }
        s[cur] = '\0';
        return cur;
    }
    template<typename type>
    bool read(type &x, int len = 0, int cur = 0, bool flag = false) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        if (flag) x = -x;
        return true;
    }
    template <typename type>
    type read(int len = 0, int cur = 0, bool flag = false, type x = 0) {
        if (!(len = read(str))) return false;
        if (str[cur] == '-') flag = true, cur++;
        for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
        return flag ? -x : x;
    }
} using FastIO::read;
const int maxn=4e4+10;
const int maxai=8e4+10;
const int maxlen=1e7+10;
int t,n,k;
ll p;
int a[maxn];
int cnt[maxai];
ll dp[maxai];

inline ll mul(ll a,ll b,ll mod){
        return ((a * b - (long long)((long long)((long double)a / mod * b + 1e-3) * mod)) % mod + mod) % mod;
}

inline ll ksm(ll a,ll n,ll mod){
    ll ans=1;
    assert(n>=0);
    while(n){
        if(n&1) ans=mul(ans,a,mod);
        a=mul(a,a,mod);
        n>>=1;
    }
    return ans%mod;
}
namespace PHR{
    typedef long long LL;
    ll n, Max = 0;
    inline ll quickpow(ll a, ll b, ll mod)
    {
        ll ret = 1;
        for(; b; b >>= 1, a = mul(a, a, mod))
            if(b & 1) ret = mul(ret, a, mod);
        return ret;
    }

    inline bool test(ll a, ll d, ll n)
    {
        ll t = quickpow(a, d, n);
        if(t == 1) return 1;
        while(d != n - 1 && t != n - 1 && t != 1) t = mul(t, t, n), d <<= 1;
        return t == n - 1;                       //这里就不用判1了,因为只可能在while前出现1的情况
    }
    int a[] = {2, 3, 5, 7, 11};
    inline bool miller_rabin(ll n)
    {
        if(n == 1) return 0;
        for(int i = 0; i < 5; ++i)        //先粗筛一遍
        {
            if(n == a[i]) return 1;
            if(!(n % a[i])) return 0;
        }
        ll d = n - 1;
        while(!(d & 1)) d >>= 1;
        for(int i = 1; i <= 5; ++i)        //搞五遍
        {
            ll a = rand() % (n - 2) + 2;//x属于[2, n - 1]
            if(!test(a, d, n)) return 0;
        }
        return 1;
    }

    inline ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    inline ll f(ll x, ll a, ll mod) {return (mul(x, x, mod) + a) % mod;}
    const int M = (1 << 7) - 1;            //我也不知道为啥M是这个数……
    ll pollard_rho(ll n)                    //倍增版!减少gcd调用次数。(好像不用判环?)
    {
        for(int i = 0; i < 5; ++i) if(n % a[i] == 0) return a[i];
        ll x = rand(), y = x, t = 1, a = rand() % (n - 2) + 2;
        for(int k = 2;; k <<= 1, y = x)
        {
            ll q = 1;
            for(int i = 1; i <= k; ++i)
            {
                x = f(x, a, n);
                q = mul(q, abs(x - y), n);
    //            if(i >= M)    //不等价!
                if(!(i & M))    //超过了2 ^ 7,再用gcd
                {
                    t = gcd(q, n);
                    if(t > 1) break;    //找到了
                }
            }
            if(t > 1 || (t = gcd(q, n)) > 1) break;    //之所以再求一遍,是处理刚开始k < M的情况
        }
        return t;
    }
    LL factor[100],total=0;
    inline void find(ll x)
    {
        if(x == 1 || x <= Max) return;
        if(miller_rabin(x)) {factor[++total]=x; return;}
        ll p = x;
        while(p == x) p = pollard_rho(x);
        while(x % p == 0) x /= p;
        find(p); find(x);
    }
}

ll x;

ll c[maxai][32];
ll PHIP;
inline ll C(ll n,ll k){
    if(n<k)return 0;
    return c[n][k];
}

inline ll phi(ll x){
    ll ans=x;
    for(ll i=2;1ll*i*i<=x;++i){
        if(x%i==0){
            ans=ans/i*(i-1);
            while(x%i==0)x/=i;
        }
    }
    if(x>1)ans=ans/x*(x-1);
    return ans;
}

signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    //clock_t c1 = clock();
    //===========================================================
    read(t);
    //init();
    while(t--){
        read(n),read(k),read(p);
        PHR::total=0;
        PHR::find(p);
        PHIP=p;
        for(int i=1;i<=PHR::total;++i)PHIP=PHIP/PHR::factor[i]*(PHR::factor[i]-1);
        c[0][0]=1;
        int mx=0;
        for(int i=1;i<=n;++i){
            int x;read(x);
            ++cnt[x];
            mx=max(mx,x);
        }
        for(int i=1;i<=n;++i){
            c[i][0]=1;;
            if(i<32)c[i][i]=1;
            for(int j=1;j<i&&j<=31;++j){
                c[i][j]=(c[i-1][j]+c[i-1][j-1]);
                while(c[i][j]-PHIP>=PHIP)c[i][j]-=PHIP;
            }
        }
        ll ans=1;
        for(int i=mx;i>=1;--i){
            int now=0;
            for(int j=i;j<=mx;j+=i){
                now+=cnt[j];
            }
            dp[i]=C(now,k);
            for(int j=i+i;j<=mx;j+=i){
                dp[i]-=dp[j];
            }
            while(dp[i]-PHIP>=PHIP)dp[i]-=PHIP;
            //dp[i]=(dp[i]%PHIP+PHIP)%PHIP;
            if(dp[i]<0)dp[i]=(dp[i]%PHIP+PHIP)%PHIP;
            ans=mul(ans,ksm(i,dp[i],p),p);
        }
        for(int i=0;i<=mx;++i){
            cnt[i]=dp[i]=0;
        }
        printf("%lld\n",ans);
    }
    //===========================================================
    //std::cerr << "Time:" << clock() - c1 << "ms" << std::endl;
    return 0;
}

样例

1
24 7 1038318
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
正确答案是519160