卡牌

upd on 2024.10.14 增加了多步容斥的原因


题目就是让你找出一组数,使其乘积是给出的质数的倍数,问有多少组这样的数

因为它是质数,一开始考虑单独求解,用质因子含一个质数的乘积减含两个数的加上含三个数的。。。然后就发现所有组合

都要算一遍,复杂的 \(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;
}
/*

*/

stars

image

posted @ 2024-10-13 19:23  _君の名は  阅读(55)  评论(7编辑  收藏  举报