題目就是讓你找出一組數,使其乘積是給出的質數的倍數,問有多少組這樣的數
因為它是質數,一開始考慮單獨求解,用質因子含一個質數的乘積減含兩個數的加上含三個數的。。。然後就發現所有組合
都要算一遍,複雜的 \(2^k-1\) (\(k\) 為質數個數)(這不就是暴力)
(在考慮了一堆性質之後發現正著容斥根本寫不了,然後就去看題解了,然後就看到了“正難則反”。。。)
(好吧,其實我是小丑)
先把那一堆性質列一下:
- 1:一個數最多有4個質數
- 2:一個數最多有一個大於43的質因數
- 3:可多次出現的質因數不超過14個
- 4:2000以內只有不到305個質數
- 5:\(\sum_{i=1}^{n}\limits \dbinom{n}{i}=2^n-1\)
考慮包含的不好處理,直接考慮不包含的,根據上面性質我們可以發現,可以把質因數從43分開,進行值域分塊,前14個去狀
壓那些數不包含,43之後的一個一個單獨處理。設 \(f_{k,x}\) 表示 \(k\) 狀態下,是 \(x\) 的倍數的數有多少,容斥即可
求方案數的話,設滿足狀態 \(k\) 的數個數為 \(num\),出現的 \(x\) 的 \(\sum f_{k,x}=cnt\),對每一個 \(x\),至少選一個,方案數 \(2^{f_{k,x}}-1\)
剩下的 \(num-cnt\) 個數選不選無所謂,方案數 \(2^{num-cnt}\)。
列舉狀態容斥時有一個要注意的點,列舉出來的狀態應該是給出質數的子集,這個地方不好理解,給大家手摸一下
假設我們給出的質數是 \(3,5,11\),那狀壓表示即為 \(10110\) ,按我們上面列舉子集,一開始是 \(00000\) 這樣表示每個數都不確定
的方案數,但我們要求的是 \(3,5,11\) 確定要選的,這裡面多了不選的方案數,所以我們減去 \(10000,00100,00010\) 等一個
不選的,後面容斥類比,再加上兩個不選的,再減去三個不選的。。。
點選檢視程式碼
#include<bits/stdc++.h>
const int mod=998244353;
const int maxn=1e6+10;
using namespace std;
int n,t,c[18005],cnt[2005],phi[350],rk[2005],tot,del[2005],f[(1<<14)+10][350],tem[(1<<14)+10];
int s[2005][6],num[2005];
bool vis[2005];
void pre()
{
for(int i=2;i<=2000;i++)
{
if(vis[i])continue;
if(!vis[i])phi[++tot]=i,rk[i]=tot;
for(int j=i*2;j<=2000;j+=i)
vis[j]=1;
}
for(int i=1;i<=2000;i++)
{
for(int j=1;j<=tot;j++)
{
if(i%phi[j])continue;
if(j<=14)del[i]|=(1<<j-1);
s[i][++num[i]]=j;
}
}
}
int qpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)ans=1ll*ans*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ans;
}
int count(int x)
{
int temp=0;
for(int i=15;i>=0;i--)
if((1<<i&x)) temp++;
return temp;
}
int main()
{
// freopen("T.in","r",stdin);
// freopen("T.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
pre();
for(int i=1,x;i<=n;i++)
{
cin>>x;
cnt[x]++;
}
for(int i=0;i<(1<<14);i++)
{
for(int j=2;j<=2000;j++)
{
if(del[j]&i)continue;
if(s[j][num[j]]>14)f[i][s[j][num[j]]]+=cnt[j];
tem[i]+=cnt[j];
}
tem[i]+=cnt[1];
}
cin>>t;
while(t--)
{
int temp=0,ans=0;
cin>>c[0];
for(int i=1,x;i<=c[0];i++)
{
cin>>c[i];
if(rk[c[i]]<=14)temp|=(1<<rk[c[i]]-1);
}
// cout<<temp<<"!"<<endl;
// cout<<tem[1]<<" "<<tem[4]<<" "<<tem[5]<<endl;
for(int i=0;i<(1<<14);i++)
{
if((i|temp)!=temp) continue;
// cout<<i<<" "<<tem[i]<<endl;
int s1=tem[i],s2=1;
for(int j=1;j<=c[0];j++)
{
if(rk[c[j]]<=14) continue;
s2=1ll*s2*(qpow(2,f[i][rk[c[j]]])-1)%mod;
s1-=f[i][rk[c[j]]];
}
s2=1ll*s2*qpow(2,s1)%mod;
if(count(i)&1) ans=1ll*(ans-s2+mod)%mod;
else ans=1ll*(ans+s2)%mod;
}
cout<<ans<<'\n';
}
return 0;
}
/*
*/