牛客挑战赛45 G 致两千年后的你
给出\(a_i\),对于集合\(S\),定义\(f_0(S)=1\)当且仅当存在任意钦定的系数\(c_i\),使得\(\sum_{i\in S}a_ic_i\equiv x\pmod P\),否则\(f_0(S)=0\)。
定义\(f_i(S)=\sum_{T\subseteq S}f_{i-1}(T)\)。
求\(f_k(U)\)。\(U\)为全集。
\(T\le 20,n\le 3*10^5\)
\(1\le x\le 10^{18}\),\(2\le P \le 10^{18}\),\(1\le k\le 10^9\)
保证\(P\)的最大质因数小于等于\(3*10^5\)。
不可拍的题。。。
考虑第一部分:如何处理这个\(f_0\)。
以下先将\(x\)变成\(x\mod P\)。
根据简单的数论知识,可以转化成如下问题:询问是否\(\gcd_{i\in S}(\gcd(a_i,P))|x\)。原因大概根据式子\(ax+by=\gcd(a,b)\)得出。
为了方便,接下来直接把\(\gcd(a_i,P)\)替换成\(a_i\)。
分解质因数,这个东西相当于:对于某个质因数\(p_i\),存在\(a_i\),满足这个质因数在\(a_i\)的指数小于等于在\(x\)中的指数。
那么每个数可以用\(01\)串\(b_i\)来表示,其中第\(j\)位表示\(p_j\)的指数是否小于等于\(x\)中\(p_j\)的指数。
于是\(f_0(S)=[or_{i\in S} b_i=U']\)。
想到这里再搞另一个问题:如何通过\(f_0\)得到\(f_k\)。
如果用集合幂级数来表示,发现就是\(f_k=f_0*I^k\)。
思考一下卷\(I^k\)是什么:先选出一个子集,在这个子集中选出一个子集,再选……选\(k-1\)遍(\(I\)是只选自己而不是子集,所以是\(k-1\))。如果将选看成加一,那么最终每个数在\([0,k-1]\)中随机,每个位置有\(k\)种选择。于是\(I^k(S)=k^{|S|}\)。
于是\(f_k(U)=\sum_{S}f_0(S)k^{n-|S|}\),提出\(k^n\),得到\(f_k(U)=k^n\sum_{S}f_0(S)k^{-|S|}\)。这启示着我们将\(k^{-1}\)作为计算\(f_0(S)\)时的系数。
回来思考一下\(f_0(S)\)如何计算。列出式子:\(f_0=\prod (1+k^{-1}x^{b_i})\)。发现\(FWT(1+k^{-1}x^{b_i})(S)=1+k^{-1}[b_0\subseteq S]\)。
一项对\(FWT(f_0(S))\)的贡献为在\(b_0\subseteq S\)处给\(f_0(S)\)乘\(1+k^{-1}\)。于是只需要计算\(1+k^{-1}\)的指数。
定义集合幂级数\(g=\sum {b_i}\),\(FWT(f_0)(S)=(1+k^{-1})^{FWT(g)(S)}\),可以算出\(g\)再算出\(f\)。
于是这道题就结束了。实现的时候要注意一点细节(拍不出来……),并且卡卡常。
比如说给gcd
的速度是很慢的,这时候需要指数取\(\min\)。还有在算\(b_i\)的时候可以加点剪枝。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300005
#define ll long long
#define mo 1000000007
#define INF 1000000000
ll gcd(ll a,ll b){
while (b){
ll k=a%b;
a=b,b=k;
}
return a;
}
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int p[N],np;
bool inp[N];
void initp(int n){
for (int i=2;i<=n;++i){
if (!inp[i])
p[++np]=i;
for (int j=1;j<=np && i*p[j]<=n;++j){
inp[i*p[j]]=1;
if (i%p[j]==0) break;
}
}
}
int n;
ll x,k,P;
ll a[N];
int b[N];
int c,d[17],e[17];
int idx(ll &n,int p){
if (n==0) return INF;
int t=0;
while (n%p==0)
n/=p,++t;
return t;
}
bool judge(ll &n,int p,int lim){
int t=0;
while (t<=lim && n%p==0)
n/=p,++t;
return t<=lim;
}
void divide(ll n){
c=0;
for (int i=1;i<=np && n!=1;++i)
if (n%p[i]==0){
d[c]=p[i];
e[c]=idx(n,p[i]);
c++;
}
if (n!=1)
d[c]=n,e[c]=1,c++;
}
void initb(){
static int bx[N];
ll xx=x;
for (int j=0;j<c;++j)
bx[j]=idx(xx,d[j]);
for (int i=1;i<=n;++i){
// ll ai=gcd(a[i],P);
ll ai=a[i];
b[i]=0;
// if (ai==0) continue;
for (int j=0;j<c;++j){
if (e[j]<=bx[j]){
b[i]|=1<<j;
continue;
}
if (judge(ai,d[j],bx[j]))
b[i]|=1<<j;
}
}
}
ll g[1<<17];
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int T;
scanf("%d%d",&n,&T);
for (int i=1;i<=n;++i)
scanf("%lld",&a[i]);
initp(300000);
while (T--){
scanf("%lld%lld%lld",&x,&k,&P);
x%=P;
divide(P);
initb();
// for (int i=1;i<=n;++i)
// printf("%d\n",b[i]);
// printf("\n");
memset(g,0,sizeof(ll)*(1<<c));
for (int i=1;i<=n;++i)
g[b[i]]++;
for (int i=1;i<1<<c;i<<=1)
for (int j=0;j<1<<c;j+=i<<1)
for (int k=0;k<i;++k)
g[j+k+i]+=g[j+k];
ll tmp=1+qpow(k);
for (int i=0;i<1<<c;++i)
g[i]=qpow(tmp,g[i]);
for (int i=1;i<1<<c;i<<=1)
for (int j=0;j<1<<c;j+=i<<1)
for (int k=0;k<i;++k)
(g[j+k+i]-=g[j+k])%=mo;
ll ans=(g[(1<<c)-1]+mo)%mo*qpow(k,n)%mo;
printf("%lld\n",ans);
}
return 0;
}