2021 牛客多校第二场补题( J

2021 牛客多校第二场补题( J

J

题意:

n n n个数,问大小为 k k k的集合的gcd的连乘

思路:
一开场就看了这题…思路也一眼出了,但是卡在欧拉降幂的大小判断上了…赛后一看rank1代码就懂了…

我的做法是容斥,好像还有后缀和等做法(但我不会)

f [ i ] f[i] f[i] g c d gcd gcd i i i i i i的倍数的 k k k大小子集的方案数, n u m [ i ] num[i] num[i]为集合中为 i i i i i i的倍数的个数,显然 f [ i ] = C ( n u m [ i ] , k ) f[i]=C(num[i],k) f[i]=C(num[i],k),然后倒序枚举值域容斥即减掉其倍数即可,就能得到gcd恰好为i的方案数,但这种做法有一个问题就是组合数位于指数,需要欧拉降幂,但是组合数需要预处理,根据拓展欧拉定理,难以判断指数 p h i ( m o d ) phi(mod) phi(mod)的大小,然后我就自闭了,但其实我们可以再维护一个 f 2 [ i ] f2[i] f2[i],但是选择模一个>phi(m)的数字,如果最后的 f 2 [ i ] f2[i] f2[i] f [ i ] f[i] f[i]不一样的话,说明要+phi(mod),真的非常巧妙…(这里rank1的代码选择模一个质数来判断,我无法理解…如果有人理解道理的话请在下方留言告诉我),但这题数据水了,有人直接模phi(mod)也过了,但是是可以hack的。

我的时间复杂度是 O ( T m a x ( x i ) l n ( x i ) + T 质 数 个 数 + T n k ) O(Tmax(x_i)ln(x_i)+T质数个数+Tnk) O(Tmax(xi)ln(xi)+T+Tnk)

事实上这题非常卡常…也可能是我的做法不好,瓶颈在于求欧拉函数那,如果用Pollaro可能快一点,本代码经过卡常跑了 830 m s + 830ms+ 830ms+才过,不保证每次都过,此外还要注意本题需要__int128

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+5;
const int maxm=4e4+5;
const ll mod=1e14;
bool v[maxn];
int prime[maxn],cnt=0,t,a[maxm];
ll f[maxm<<1],f2[maxm<<1];
ll c[maxm][32],P,c2[maxm][32];
bool is[maxm][32],ismod[maxm<<1];
ll phi(ll x){ 
    ll ans=x;
    for(int i=1;1ll*prime[i]*prime[i]<=x&&i<=cnt;++i){ 
        if(x%prime[i]==0){ 
            ans-=ans/prime[i];
            while(x%prime[i]==0)x/=prime[i];
        }
    }
    if(x>1)ans-=ans/x;
    return ans;
}
void init(){ 
    for(int i=2;i<maxn;++i){ 
        if(!v[i])prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<maxn;++j){ 
            v[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
    for(int i=0;i<=maxm-5;++i){ 
        c2[i][0]=1;
        for(int j=1;j<=min(i,30);++j){ 
            c2[i][j]=c2[i-1][j-1]+c2[i-1][j];
            if(c2[i][j]>=mod)c2[i][j]-=mod;  
        }
    }
}
void init2(ll phi,int k,int n){ 
    c[0][0]=1;
    for(int i=1;i<=30;++i)c[i][i]=1;
    for(int i=0;i<=n;++i){ 
        c[i][0]=1;
        for(int j=1;j<=min(i,k);++j){ 
            c[i][j]=c[i-1][j-1]+c[i-1][j];
            if(c[i][j]>=phi){ 
                c[i][j]-=phi;
            }
        }
    }
}
ll mul(ll a,ll b,ll mod){ 
    return __int128(a)*b%mod;
}
ll mypow(ll a,ll b){ 
    ll ans=1;
    while(b){ 
        if(b&1)ans=mul(ans,a,P);
        a=mul(a,a,P);
        b>>=1;
    }
    return ans;
}
void read(int &num)
{
	num=0;
	char ch=getchar();
	while (ch<48 || ch>57) ch=getchar();
	while (ch>=48 && ch<=57) num=num*10+ch-48,ch=getchar();
}
void readll(ll &num)
{
	num=0;
	char ch=getchar();
	while (ch<48 || ch>57) ch=getchar();
	while (ch>=48 && ch<=57) num=num*10+ch-48,ch=getchar();
}
int main(){ 
    init();
	read(t);
    int s,k;
    while(t--){ 
		read(s);
		read(k);
		readll(P);
        int mx=0;
        ll x=phi(P);
        init2(x,k,s);
        for(int i=1;i<=s;++i){ 
            read(a[i]);
			mx=max(mx,a[i]);
            f[a[i]]++;
        }
        for(int i=1;i<=mx;++i){ 
            for(int j=2*i;j<=mx;j+=i)
                f[i]+=f[j];
        }
        for(int i=1;i<=mx;++i)f2[i]=c2[f[i]][k],f[i]=c[f[i]][k];
        int M=(mx+1)/2;
        for(int i=M;i>=1;--i){ 
            for(int j=2*i;j<=mx;j+=i){ 
                f[i]-=f[j];
                if(f[i]<0)
                    f[i]+=x;
                f2[i]-=f2[j];
                if(f2[i]<0)
                    f2[i]+=mod;
            }   
        }
        ll ans=1;
        for(int i=1;i<=mx;++i){ 
            if(f[i]!=f2[i])f[i]+=x;
                ans=mul(ans,mypow(i,f[i]),P);
        }
        for(int i=1;i<=mx;++i)f[i]=f2[i]=0;
        cout<<ans<<"\n";
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值