【容斥】模拟赛T1

【题目描述】
小S决定送给小萌一个礼物。

这是一个特殊的礼物,它是一个集合,且集合所有的数的gcd为1,所有数的lcm为m。

小S他有多少种不同的送礼方案呢。两个送礼方案不同当且仅当存在某个x在一个方案中而不在另一个中。

答案对998244353取模。

【输入】
一行一个正整数m。

【输出】
输出答案对998244353取模。
在这里插入图片描述

【思路】

1.初步分析
首先,集合内的数一定是m的约数,于是第一个算法就是进行暴力搜索,判断集合合法性。由这个显然的结论我们可以知道,无论是什么算法,都必须对m进行质因数分解。但是m会达到 1 0 18 10^{18} 1018的规模,我们即使预处理素数也无法承受这样的数据规模,但是60分足以通过,我们姑且先不考虑这个问题。同时还有一些有趣的结论,m的不同质因数个数不超过15,m的约数个数不超过十万。

2.容斥
我们发现,如果把m表示为 m = ∏ i k p i a i m=\prod_{i}^{k}p_{i}^{a_{i}} m=ikpiai,其中p为质因数,那么满足合法集合必须满足:对于每一个i,集合必须存在一个数满足 p i p_{i} pi项的次数为0,存在一个数满足 p i p_{i} pi项次数恰为 a i a_{i} ai,其余数的 p i p_{i} pi项次数属于 [ 0 , a i ] [0,a_{i}] [0,ai]。我们可以把上述限制提炼为2k个限制,直接计算不好计算,我们考虑容斥。注意到让集合中的数满足以上限制的基础上进行统计十分困难,但是如果我们钦定一些限制不被满足,计算方案就比较简单。所以我们考虑计算出至少有i个限制不被满足的方案,然后进行容斥:
a n s = ∑ i 2 k ( − 1 ) i ( 2 ∏ j k R j − l j + 1 − 1 ) ans=\sum_{i}^{2k}(-1)^{i}(2^{\prod_{j}^{k}R_{j}-l_{j}+1}-1) ans=i2k(1)i(2jkRjlj+11)
其中 R j R_{j} Rj表示集合中的数每一个质因子次数的上界,可以是 a j a_{j} aj(满足限制)或 a j − 1 a_{j}-1 aj1(不满足限制), l j l_{j} lj表示集合中的数每一个质因数的次数的下界,可以是0(满足限制)或1(不满足限制)。而集合的最大可能总数的计算可以通过乘法原理计算,即 ∏ j k R j − l j + 1 \prod_{j}^{k}R_{j}-l_{j}+1 jkRjlj+1。而对于这其中的每个数,我们可以选或不选(满足"至少"的计算),除去空集的方案就是上述式子,配上容斥系数即可完成计算。也就是说,对于每个质因数,我们可以进行如下操作:

1.上下界限制均满足
2.上界满足
3.下界满足
4.上下界均不满足

我们可以通过搜索算法暴力枚举上述情况,复杂度为O( 4 2 k 4^{2k} 42k),复杂度有一点小爆炸。考虑优化,我们注意到 ,统计方案时我们只关心区间长度,而当前质因数次数区间为 [ 1 , a i ] [1,a_{i}] [1,ai] [ 0 , a i − 1 ] [0,a_{i}-1] [0,ai1]对答案的贡献是完全相同的,所以我们可以把两者合并。复杂度优化为O( 3 2 k 3^{2k} 32k),可以通过。

3.m的分解
事实上,基于上述算法,我们发现,我们不再关心m的质因数具体是哪个数,而只关心m某个质因数的次数。这就有效降低了分解m的难度。考虑预处理出1e6以内所有质数,对m进行质因数分解。由于m只有1e18那么m最多有两个大于1e6的质因数,可能是以下三种情况:
1. m = p 2 m=p^{2} m=p2
2. m = p m=p m=p
3. m = p q m=pq m=pq
对于第一种情况,我们可以把m开根判断。对于第二种情况,我们可以利用素数测试判断(代码中是用费马小定理进行枚(luan)举(gao)判断的)。如果不属于这两种情况,那么就属于第三种情况,那么问题就迎刃而解了。

代码:

#include<bits/stdc++.h>
#define re register
#define add(a,b) ((a)+(b)>=mod?(a)+(b)-mod:(a)+(b))
#define dec(a,b) ((a)>=(b)?(a)-(b):((a)-(b)+mod))
#define mul(a,b) ((long long)(a)*(b)%mod)
using namespace std;
const int N=1e6+5;
const int mod=998244353;
int n,b,c;
long long m;
inline int red()
{
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data*w;
}
int pcnt=0,p[N],cnt[N],tot=0;
bool vis[N];
inline int ksm(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=mul(ret,a);
		b>>=1;
		a=mul(a,a);
	}
	return ret;
}
long long qpow(long long a,long long b,long long mod)
{
	long long ret=1;
	while(b)
	{
		if(b&1)ret=mul(ret,a);
		a=mul(a,a);
		b>>=1;
	}
	return ret;
}
void pre()
{
	for(int re i=2;i<=1e6;i++)
	{
		if(!vis[i])p[++pcnt]=i;
		for(int re j=1,x=p[j]*i;j<=pcnt&&x<=1e4;j++,x=p[j]*i)
		{
			vis[x]=1;
			if(i%p[j]==0)break;
		}
	}
}
inline bool lsr(long long m)
{
	for(int re i=1;i<=100;i++)
		if(qpow(p[i],m-1,m)!=1)return 0;
	return 1;
}
void divide(long long m)
{
	for(int re i=1;i<=pcnt;i++)
		if(m%p[i]==0)
		{
			++tot;
			while(m%p[i]==0)m/=p[i],cnt[tot]++;
		}
	if(m!=1)
	{
		if(lsr(m))cnt[++tot]=1;
		else if(pow((long long)sqrt(m),2)==m)cnt[++tot]=2;
		else cnt[++tot]=1,cnt[++tot]=1;
	}
}
long long ans=0;
void dfs(int pos,int s,int now)
{
	if(pos==tot+1)
	{
		ans+=mul(s,ksm(2,now)-1);
		ans=(ans%mod+mod)%mod;
		return;
	}
	dfs(pos+1,s,now*(cnt[pos]+1));
	dfs(pos+1,-s*2,now*cnt[pos]);
	if(cnt[pos]!=1)dfs(pos+1,s,now*(cnt[pos]-1));
}
int main()
{
	pre();
	scanf("%lld",&m);
	divide(m);
	dfs(1,1,1);
	cout<<ans;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值