NOIP模拟赛 乘积(容斥计数)

这篇博客介绍了如何解决一个NOIP模拟赛中的乘积问题,通过应用容斥计数原理和莫比乌斯反演。首先使用莫比乌斯反演进行优化,将复杂度降低到O(nlogn),然后分解质因数并计算每个质因数及其幂次的贡献,确保在计算过程中避免整数溢出。作者提供了AC代码作为解决方案。
摘要由CSDN通过智能技术生成

在这里插入图片描述

n < = 1 e 9 , m < = 3 e 5 n<=1e9,m<=3e5 n<=1e9,m<=3e5

题解:在这里插入图片描述

具体来说就是先类莫比乌斯反演一波(这个关于倍数的莫比乌斯反演过程可以用经典的补集转化 O ( n log ⁡ n ) O(n\log n) O(nlogn)代替,于是就NOIP了),然后就变成求 d ∣ g c d d | gcd dgcd p r o d prod prod,然后可以分别求出每个质因数及质因数的幂的贡献然后合并,完。
注:质因数的幂注意开 l o n g l o n g long long longlong,中间乘爆也会进for的。

A C   C o d e AC\ Code AC Code:

#include<bits/stdc++.h>
#define maxn 300005
#define mod 998244353
#define md 998244352
using namespace std;

int n,m,ans=1;
int pr[maxn],cnt_pr,vis[maxn],f[maxn],g[maxn],pw[maxn];
int Pow(int base,int k,int p){
	int ret=1;
	for(;k;k>>=1,base=1ll*base*base%p)
		if(k&1)
			ret=1ll*ret*base%p;
	return ret;
}
int calc(int m){
	int ret = 1 , sum = pw[m];
	for(int i=0;i<cnt_pr && pr[i]<=m;i++){
		int t = 0;
		for(long long j=pr[i];j<=m;j*=pr[i])//!!! 129900 * 129900 -> int overflow
			t = (1ll * t + sum - pw[m - m/j]) % md;
		ret = 1ll * ret * Pow(pr[i],(t+md)%md,mod) % mod;
	}
	return ret;
}

int main(){
	scanf("%d%d",&n,&m);
	pw[1] = 1;
	for(int i=2;i<=m;i++){
		if(!vis[i]) pr[cnt_pr++] = i;
		for(int j=0;pr[j]*i<=m;j++){
			vis[i*pr[j]] = 1;
			if(i%pr[j] == 0)break;
		}
		pw[i] = Pow(i,n,md);
	}
	
	for(int i=1,nxt;i<=m;i=nxt+1){
		nxt = m/(m/i);
		int t = calc(m/i);
		for(int j=i;j<=nxt;j++)
			g[j] = 1ll * t * Pow(j,pw[m/i],mod) % mod;
	} 
	for(int i=m;i>=1;i--){
		for(int j=2*i;j<=m;j+=i)
			g[i] = (1ll * g[i] * f[j]) % mod;
		f[i] = Pow(g[i] , mod-2 , mod);
	}
	for(int i=1;i<=m;i++)
		ans = 1ll * ans * Pow(g[i],i,mod) % mod;
	printf("%d\n",(ans+mod)%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值