2021牛客暑期多校训练营2 J-Product of GCDs(数论+计数)

J-Product of GCDs

Code1

对于每个质数以及每个质数的次幂单独考虑他们的贡献,由于多次使用快速幂导致TLE

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
    T res=0;T fg=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res*fg;
}

int prime[10000010],cnt,is[10000010];
void initP()
{
    for(int i=2;i<=10000000;i++)
    {
        if(!is[i]) prime[++cnt]=i;
        for(int j=1;prime[j]<=10000000/i;j++)
        {
            is[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
int n,m;
ll P,Phi;
ll Cphi(ll v)
{
    ll ans=v;
    for(int i=1;prime[i]<=v/prime[i];i++)
        if(v%prime[i]==0)
        {
            ans=ans/prime[i]*(prime[i]-1);
            while(v%prime[i]==0) v/=prime[i];
        }
    if(v>1) ans=ans/v*(v-1);
    return ans;
}
const int N=8e4+10;
ll C[N][35];
void mod(ll &v){if(v>=Phi) v-=Phi;}
void initC()
{
    for(int i=0;i<=n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=min(i,m);j++)
        {
            C[i][j]=C[i-1][j]+C[i-1][j-1],mod(C[i][j]);
        }
    }
}
ll mul(ll a,ll b){return(__int128)a*b%P;}
ll qmi(ll a,ll b)
{
    ll v=1;
    while(b)
    {
        if(b&1) v=mul(v,a);
        b>>=1;
        a=mul(a,a);
    }
    return v;
}
int a[N];
int main()
{
    initP();
    int Tc=rd();
    while(Tc--)
    {
        n=rd(),m=rd(),P=rd<ll>(),Phi=Cphi(P);
        initC();
        
        for(int i=1;i<=n;i++) a[i]=rd();
        ll ans=1;
        for(int p=1;p<=8000;p++)
        {
            static int b[N];for(int i=1;i<=n;i++) b[i]=0;
            
            for(int i=1;i<=n;i++) while(a[i]%prime[p]==0) a[i]/=prime[p],b[i]++;
        
            // 桶排序
            static int c[30];memset(c,0,sizeof c);
            
            int maxn=*max_element(b+1,b+1+n);
            if(maxn==0) continue;
            for(int i=1;i<=n;i++) c[b[i]]++;
            int cnt=0;
            for(int i=1;i<=maxn;i++) while(c[i]--) b[++cnt]=i;
            for(int i=1;i<=cnt;i++)
            {
                ll pw=C[cnt-i][m-1];
                ans=mul(ans,qmi(qmi(prime[p],pw),b[i]));
            }
            
        }
        printf("%lld\n",ans);
        
    }
}
Code2

考虑分质因子统计,计算 f ( p , c ) f_{(p,c)} f(p,c)表示至少包含 p c p^c pc 的数个数,方案数为
( f ( p , c ) k ) \begin{pmatrix} f_{(p,c)}\\ k \end{pmatrix} (f(p,c)k)

注意需要计算得到的实际上是指数,因此我们需要对于𝜑(𝑷) 取模
(扩展)欧拉定理学习笔记
在这里插入图片描述
好像很多同学关心加不加phi的问题,,我发现欧拉降幂挺多题其实都没故意卡这点也就是记住对𝜑(𝑷)取模即可~

通过分解质因数暴力求𝜑(𝑷)即可,只需要预处理√𝑃 内的质数,复杂度为𝑂(√𝑃+𝑇 √𝑃/(𝑙𝑜𝑔 𝑃))
𝜑(𝑷)不是质数,但是鉴于𝑘较小,组合数直接𝑂(𝑛𝑘)递推即可(为此 𝑛 开得比较小)

#include<bits/stdc++.h>

using namespace std;
using ll=long long;
template <class T=int> T rd()
{
    T res=0;
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res;
}

int prime[10000010],cnt,is[10000010];
void initP()
{
    for(int i=2;i<=10000000;i++)
    {
        if(!is[i]) prime[++cnt]=i;
        for(int j=1;prime[j]<=10000000/i;j++)
        {
            is[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
int n,m;
ll P,Phi;
ll Cphi(ll v)
{
    ll ans=v;
    for(int i=1;prime[i]<=v/prime[i];i++)
        if(v%prime[i]==0)
        {
            ans=ans/prime[i]*(prime[i]-1);
            while(v%prime[i]==0) v/=prime[i];
        }
    if(v>1) ans=ans/v*(v-1);
    return ans;
}
const int N=8e4+10;
ll C[N][35];
void mod(ll &v){if(v>=Phi) v-=Phi;}
void initC()
{
    for(int i=0;i<=n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=min(i,m);j++)
        {
            C[i][j]=C[i-1][j]+C[i-1][j-1],mod(C[i][j]);
        }
    }
}
ll mul(ll a,ll b){return(__int128)a*b%P;}
ll qmi(ll a,ll b)
{
    ll v=1;
    while(b)
    {
        if(b&1) v=mul(v,a);
        b>>=1;
        a=mul(a,a);
    }
    return v;
}
int a[N];
int main()
{
    initP();
    int Tc=rd();
    while(Tc--)
    {
        n=rd(),m=rd(),P=rd<ll>(),Phi=Cphi(P);
        initC();
        memset(a,0,sizeof a);
        for(int i=1;i<=n;i++) a[rd()]++;
        ll ans=1;
        for(int p=1;p<=8000;p++)
        {
            ll res=0;
            for(int i=prime[p];i<N;i*=prime[p])
            {
                int c=0;
                for(int j=i;j<N;j+=i) c+=a[j];
                if(c<m) break;
                res+=C[c][m],mod(res);
                if(1ll*i*prime[p]>=N) break;
            }
            if(res) ans=mul(ans,qmi(prime[p],res));
            
        }
        printf("%lld\n",ans);
        
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值