正题
题目链接:https://www.luogu.com.cn/problem/P8292
题目大意
有nnn张卡牌,第iii张上的数字是sis_isi。mmm次询问给出cic_ici个质数,要求选择一些卡使得这些卡的乘积是这些质数的倍数,求方案数。
1≤n≤106,1≤si≤2000,1≤m≤1500,∑i=1mci≤180001\leq n\leq 10^6,1\leq s_i\leq 2000,1\leq m\leq 1500,\sum_{i=1}^m c_i\leq 180001≤n≤106,1≤si≤2000,1≤m≤1500,∑i=1mci≤18000
解题思路
对于一个数字xxx来说,大于x\sqrt xx的质因子只会有一个,也就是对于大于所有的si\sqrt s_isi的质数ppp,它们的倍数之间不会产生重复,可以分开计数。
而小于si\sqrt s_isi的质数个数只有141414个,我们可以考虑状压。
处理出gsg_sgs表示在集合sss中的质数都不选择(即没有这些数的倍数)时的数字个数,然后再预处理出一个fi,sf_{i,s}fi,s表示质数iii的倍数中集合sss中的质数都不选择时的数字个数。
那么对于一个询问,我们先处理出小于等于s\sqrt ss的质数被加进去的状态SSS,然后对于大于s\sqrt ss的每个质数ppp,那么答案就是
∑T⊆S(2gT×∑p(1−12fp,T))(−1)∣T∣\sum_{T\sube S}\left(2^{g_T}\times \sum_{p}\left(1-\frac{1}{2^{f_{p,T}}}\right)\right)(-1)^{|T|}T⊆S∑(2gT×p∑(1−2fp,T1))(−1)∣T∣
然后求和就可以了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cctype>
#define ll long long
using namespace std;
const ll N=2100,M=1e6+10,L=14,P=998244353;
ll n,m,cnt,pri[320],pw[M],iw[M],ct[1<<L];
ll w[N],f[1<<L][320],g[1<<L],q[N*10],v[N];
vector<ll> r;bool usd[N];
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();}return x*f;
}
void Prime(){for(ll i=2;i<N;i++){if(!v[i])pri[cnt]=i,cnt++,v[i]=cnt-1;for(ll j=0;j<cnt&&i*pri[j]<N;j++){v[i*pri[j]]=1;if(i%pri[j]==0)break;}}return;
}
signed main()
{Prime();n=read();pw[0]=iw[0]=1;for(ll i=1;i<=n;i++)pw[i]=pw[i-1]*2ll%P;for(ll i=1;i<=n;i++)iw[i]=iw[i-1]*(P+1)/2ll%P;for(ll i=1,x;i<=n;i++)x=read(),w[x]++;ll MS=(1<<L);for(ll s=1;s<MS;s++)ct[s]=ct[s-(s&-s)]+1;for(ll s=0;s<MS;s++){r.clear();memset(usd,0,sizeof(usd));for(ll i=0;i<L;i++)if((s>>i)&1){for(ll j=pri[i];j<=2000;j+=pri[i])usd[j]=1;}for(ll i=1;i<=2000;i++)if(!usd[i])g[s]+=w[i],r.push_back(i);g[s]=pw[g[s]];for(ll i=L;i<cnt;i++){f[s][i]=0;for(ll j=0;r[j]*pri[i]<=2000;j++)f[s][i]+=w[r[j]*pri[i]];ll k=f[s][i];f[s][i]=iw[k]*(pw[k]-1)%P;}}m=read();while(m--){ll k=read();ll t=0,tot=0,ans=0;for(ll i=1,x;i<=k;i++){x=read();if(v[x]<L)t|=1<<v[x];else q[++tot]=v[x];}sort(q+1,q+1+tot);tot=unique(q+1,q+1+tot)-q-1;for(ll s=t;s>=0;s=(s-1)&t){ll k=(ct[s]&1)?(P-g[s]):g[s];for(ll i=1;i<=tot;i++)k=k*f[s][q[i]]%P;(ans+=k)%=P;if(!s)break;}printf("%lld\n",(ans+P)%P);}return 0;
}