题解 I. gcd -“山大地纬杯”第十二届山东省ICPC大学生程序设计竞赛(正式赛)

传送门

VP 的时候失误推错太多次了,写个博客总结一下


【大意】

求所有长度为 m 且和为 n 的正整数序列 a 的贡献和。其中,每个数列的贡献为 i>1gcd(ai1,ai)w(ai)


【分析】

考虑从 m 个位置中,选择两个相邻的位置分别填写 i,j ,则方案数为 (m1)

剩余的 m2 个正整数的和为 nij ,则等价于模型“nij 个小球放入 m2 个盒子里,要求盒子非空”。由隔板法,方案数为 (nij1m3)

因此,选中的两个位置分别填写 i,j 后,对答案的贡献为 (m1)(nij1m3)gcd(i,j)w(j)

我们只需要枚举正整数 i,j 即可:

res=i,j1(m1)(nij1m3)gcd(i,j)w(j)

注意到公式中重复出线的 i+j 项,我们可以直接枚举 k=i+j

res=(m1)k=2n(nk1m3)i+j=kgcd(i,j)w(j)

考虑 gcd(i,j)=gcd(kj,j)=gcd(k,j) ,因此有:

i+j=kgcd(i,j)w(j)=j=1k1gcd(k,j)w(j)=j=1kgcd(k,j)w(j)kw(k)

因此,现在仅需要求解 j=1kgcd(k,j)w(j) 即可求得答案

w(j)=tctjtj=1kgcd(k,j)w(j)=tctj=1kgcd(k,j)jt

于是问题简化为如何求解 j=1kgcd(k,j)jt


对于代求式子,我们枚举其 gcd ,并莫比乌斯反演,得到:

j=1kgcd(k,j)jt=gkgj=1k[gcd(k,j)=g]jt=gkgj=1kg[gcd(kg,j)=1](jg)t=gkgt+1j=1kgdkgdjμ(d)jt=gdkgt+1μ(d)j=1kgd(jd)t=gdkgt+1(μ(d)dt)j=1kgdjt=gdq=kgt+1(μ(d)dt)St(q)

其中 St(n)=i=1nit=i=0t+1At,iniAt,i 是自然数 t 次幂和的第 i 项系数

代入得到:

j=1kgcd(k,j)jt=gdq=kgt+1(μ(d)dt)St(q)=gdq=kgt+1(μ(d)dt)i=0t+1At,iqi=i=0t+1At,igdq=kgt+1(μ(d)dt)qi

最后这个求和项 gdq=kgt+1(μ(d)dt)qi 是积性函数 idt+1,μidt,idi 的迪利克雷卷积,显然也是积性函数

不妨记上述积性函数为 ft,i ,则有:

ft,i(pe)=x+y+z=e(px)t+1(μ(py)(py)t)(pz)i=x+z=e(px)t+1(pz)ix+z=e1(px)t+1(pz)ipt=peix=0e(px)t+1ip(e1)i+tx=0e1(px)t+1i(it+1)

t+1i>0 时,有:

ft,i(pe)=pei1p(e+1)(t+1i)1pt+1ip(e1)i+t1pe(t+1i)1pt+1i=pei(1pti)pe(t+1)+ti(p1)1pti+1

t+1i=0 时,有:

ft,i(pe)=pei(e+1)pei+tie

这种式子显然可以欧拉筛:

对于某个质数 p ,直接算出它所有幂次的值,然后欧拉筛过程中额外维护最小质因数出线的次数。转移的时候直接查表就行。

复杂度为原欧拉筛复杂度 O(n) ,加上处理质数幂次位置的结果。经一些不严谨的半猜半分析,后半部分的复杂度应该也是 O(n)

由于题目所给的式子仅有 1~3 次项,因此我们只要预处理 t=1,2,3;it+1ft,i 即可筛出所有答案

考虑到 {S1(n)=n(n+1)2=12n2+12nS2(n)=n(n+1)(2n+1)6=13n3+12n2+16nS3(n)=[n(n+1)2]2=14n4+12n3+14n2

因此,仅需要处理 (t,i){(1,1),(1,2),(2,1),(2,2),(2,3),(3,2),(3,3),(3,4)}8 项即可


【代码】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Lim=1e7, MAXN=Lim+10, P=1e9+7;

inline int kpow(int a, int x) { int ans=1; for(; x; x>>=1, a=(ll)a*a%P) if(x&1) ans=(ll)ans*a%P; return ans; }
inline int inv(int a) { return kpow(a, P-2); }
inline int norm(int v) { return v>=P?v-P:v; }
struct Z {
	int v;
	inline Z(int v_=0):v(norm(v_)) {}
	inline Z& operator += (const Z& x) { v=norm(v+x.v); return *this; }
	inline Z& operator -= (const Z& x) { v=norm(v+P-x.v); return *this; }
	inline Z& operator *= (const Z& x) { v=(ll)v*x.v%P; return *this; }
	inline Z operator + (const Z& x) const { Z y=*this; return y+=x; }
	inline Z operator - (const Z& x) const { Z y=*this; return y-=x; }
	inline Z operator * (const Z& x) const { Z y=*this; return y*=x; }
	inline Z operator ! () const { return inv(v); }
	inline operator int() const { return v; }
};
const int SIZ=8;
struct multi_F : public array<Z, SIZ> {
	inline multi_F() { fill(0); }
	inline multi_F& operator += (const multi_F &x) {
		for(int i=0; i<SIZ; ++i) at(i)+=x[i];
		return *this;
	}
	inline multi_F& operator -= (const multi_F &x) {
		for(int i=0; i<SIZ; ++i) at(i)-=x[i];
		return *this;
	}
	inline multi_F& operator *= (const multi_F &x) {
		for(int i=0; i<SIZ; ++i) at(i)*=x[i];
		return *this;
	}
	inline multi_F operator + (const multi_F &x) const { multi_F y=*this; return y+=x; }
	inline multi_F operator - (const multi_F &x) const { multi_F y=*this; return y-=x; }
	inline multi_F operator * (const multi_F &x) const { multi_F y=*this; return y*=x; }
};
inline multi_F f(int p, int k, ll pk, multi_F lst) {
	static int t[] = {1, 1, 2, 2, 2, 3, 3, 3};
	static int i[] = {1, 2, 1, 2, 3, 2, 3, 4};
	for(int j=0; j<SIZ; ++j) {
		int dif = t[j] + 1 - i[j];
		int a = kpow(p, dif+P-2);
		if(dif) {
			int b = (kpow(pk, i[j]) * (1ll-a) - kpow(pk, t[j]+1) * (p-1ll)%P* a)%P;
			lst[j] = (ll)b * inv(1-(ll)a*p%P)%P + P;
		}
		else
			lst[j] = kpow(pk, i[j]) * (k + 1ll - (ll)a*k%P) %P+P;
	}
	return lst;
}
int minp[MAXN], fr[MAXN], prime[MAXN/10], cntprime;
multi_F fi[MAXN];
inline void sieve() {
	fi[1].fill(1);
	for(int i=2; i<=Lim; ++i) {
		if(!minp[i]) {
			prime[++cntprime]=i;
			multi_F val=fi[1];
			for(ll pk=i, k=1; pk<=Lim; pk*=i, ++k) {
				fi[pk]=val=f(i, k, pk, val);
				minp[pk]=i;
				fr[pk]=1;
			}
		}
		for(int j=1; j<=cntprime; ++j) {
			int x=i*prime[j];
			if(prime[j] > minp[i] || x>Lim) break;
			minp[x]=prime[j];
			if(prime[j]==minp[i]) fr[x]=fr[i];
			else fr[x]=i;
			fi[x]=fi[fr[x]]*fi[x/fr[x]];
		}
	}
}

int n, m, p, q, r;
Z fact[MAXN], inft[MAXN];
inline Z C(int n, int m) { return m<0||m>n?Z(0):fact[n]*inft[m]*inft[n-m]; }
Z h[MAXN];
inline void init() {
	sieve();
	cin>>n>>m;
	cin>>p>>q>>r;
	Z rr=Z(r)*!Z(2);
	Z qq=Z(q)*!Z(6);
	Z pp=Z(p)*!Z(4);
	for(int i=1; i<=Lim; ++i) {
		Z g1 = (fi[i][0]*Z(1) + fi[i][1]*Z(1)) * rr;
		Z g2 = (fi[i][2]*Z(1) + fi[i][3]*Z(3) + fi[i][4]*Z(2)) * qq;
		Z g3 = (fi[i][5]*Z(1) + fi[i][6]*Z(2) + fi[i][7]*Z(1)) * pp;
		h[i]=g1+g2+g3;
	}
	
	fact[0]=1;
	for(int i=1; i<=Lim; ++i) fact[i]=fact[i-1]*Z(i);
	inft[Lim]=!fact[Lim];
	for(int i=Lim; i; --i) inft[i-1]=inft[i]*Z(i);
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	init();
	Z res=0;
	for(int k=2; k<n; ++k) {
		Z tmp=((Z(p)*Z(k)+Z(q))*Z(k)+Z(r))*Z(k)*Z(k);
		res+=C(n-k-1, m-3)*(h[k]-tmp);
	}
	res*=Z(m-1);
	cout<<res;
	return 0;
}
posted @   JustinRochester  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示