CCPC广州2021 K.Magus Night

这题神啊…虽然感觉只是步骤很多而已…是因为这题不会没有金也认了啊Orzzz…

  • 题目大意:有长度为n的序列,每个数字在 [ 1 , m ] [1,m] [1,m]之间,在 m n m^n mn种序列中,定义"好"的序列为:序列的 l c m > = p , g c d < = q lcm>=p,gcd<=q lcm>=p,gcd<=q。一个序列的权值定义为:所有元素的乘积。对于所有"好"的序列,求权值和。
  • n < = 998244351 , m , p , q < = 2 e 5 n<=998244351,m,p,q<=2e5 n<=998244351,m,p,q<=2e5

题目要求求 l c m > = p , g c d < = q lcm>=p,gcd<=q lcm>=p,gcd<=q的好序列。
所以我们将所有序列分为三类:
A : g c d > q A:gcd>q A:gcd>q
B : g c d < = q , l c m < p B:gcd<=q,lcm<p B:gcd<=q,lcm<p
C : g c d < = q , l c m > = p C:gcd<=q,lcm>=p C:gcd<=q,lcm>=p(目标要求)
由于全集是,所有数组的和,为 ( m ∗ ( m + 1 ) / 2 ) n (m*(m+1)/2)^n (m(m+1)/2)n,所以只要求出AB集合的权值减去即可。

对于 A A A集合来说:
考虑 d p a [ i ] dpa[i] dpa[i]表示,gcd恰好为i的序列权值和。
由于 g c d % i = = 0 gcd\%i==0 gcd%i==0的序列权值和可以快速得到 ( i ∗ ( m / i ∗ ( m / i + 1 ) ) / 2 ) n (i*(m/i*(m/i+1))/2)^n (i(m/i(m/i+1))/2)n
所以我们从i开始,枚举i的倍数减去即可(基本容斥)

对于 B B B集合来说:
考虑 d p b [ i ] dpb[i] dpb[i]表示, l c m = i , g c d = 1 lcm=i,gcd=1 lcm=i,gcd=1的序列权值和。
那么可以把i分解质因数,对于每一个质因子分别考虑。
可以发现,对于一种质因子 p p p,出现了 c c c次,一定要存在 p 0 , p c p^0,p^c p0,pc两种数字才可满足。
所以我们容斥一下,把四种情况的贡献都算出来就行了。
不同质因子之间答案相乘即可。

我们有了 d p b dpb dpb数组,就可以通过枚举 g c d gcd gcd,通过枚举倍数来统计B集合,复杂度为调和级数。
最后使用全集-AB集合的权值和即为答案。

代码实现如下:

#pragma GCC optimize("O3")
#pragma GCC target("avx2")
#include<bits/stdc++.h>
#define ll long long
#define maxn 400005
#define inf 1e9
#define ins insert
#define pb push_back
#define vi vector <int>
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}

ll dpa[maxn],dpb[maxn],P[maxn],n,m,p,q,mn[maxn],ANS;
bool is[maxn];
const ll mod=998244353;

inline ll pw(ll a,ll b)
{
	ll ans=1,base=a;
	while(b)
	{
		if(b&1) ans=(ans*base)%mod;
		base=(base*base)%mod; b>>=1;
	}
	return ans;
}

int main()
{
	n=read(); m=read(); p=read(); q=read();
	rep(i,1,2*m) P[i]=pw(i,n);
	per(i,m,1)
	{
		dpa[i]=pw((m/i*(m/i+1)/2)%mod*i%mod,n);
		for(ll j=2*i;j<=m;j+=i) dpa[i]=(dpa[i]-dpa[j]+mod)%mod;
	}
	rep(i,2,maxn-5)
	{
		if(is[i]) continue; mn[i]=i;
		for(ll j=2*i;j<=maxn-5;j+=i) is[j]=1,mn[j]=i;
	}
	
	rep(i,1,m)
	{
		int tmp=i; ll ans=1;
		while(tmp!=1)
		{
			int x=mn[tmp],ret=0,nw=1;
			while(tmp%x==0)
			{
				ret+=nw;
				tmp/=x;
				nw*=x;
			}
			ll qq=(P[ret+nw]-P[ret+nw-1]-P[ret]+P[ret-1]);
			qq=(qq%mod+mod)%mod; ans=(ans*qq)%mod;
		}
		dpb[i]=ans;
	}
	
	ANS=pw(((m*(m+1))/2)%mod,n);
	rep(i,q+1,m) ANS=(ANS-dpa[i]+mod)%mod;
	rep(i,1,q)
	{
		for(ll j=i,k=1;j<p;j+=i,k++)
			ANS=(ANS-P[i]*dpb[k]%mod+mod)%mod;
	}
	cout<<ANS<<endl;
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值