【BZOJ4833】[Lydsy1704月赛] 最小公倍佩尔数(神仙数学题)

点此看题面

大致题意: 已知(e_i=e_{i-1}+2f_{i-1},f_i=e_{i-1}+f_{i-1}),设(g(n)=lcm_{i=1}^nf(n)),求(sum_{i=1}^ni imes g(i))

前言

神仙题,这种东西我怎么可能想得到。。。

我不会推式子,我只是式子的搬运工。

递推式的化简

好吧,这一部分大概是我唯一自己推出来的式子。。。(也因此我的这一步要比别人麻烦许多)

考虑把(e_i)消去,然后我就采用了代入消元法,递归代入得到:

[f_i=f_{i-1}+e_{i-1}=f_{i-1}+2f_{i-2}+e_{i-2}=f_{i-1}+2f_{i-2}+2f_{i-3}+e_{i-3}=... ]

然后我们发现当最终(e)的下标变成(0)时,(f_i)就是这样一个递推式。

[f_i=f_{i-1}+2sum_{k=1}^{i-2}f_k ]

然而(sum)这项毕竟还是太麻烦了,考虑把它替换掉:

[2sum_{k=1}^{i-2}f_k=(f_{i-2}+2sum_{k=1}^{i-3}f_k)+f_{i-2}=f_{i-1}+f_{i-2} ]

于是最终我们就得到了一个很简单的式子:

[f_i=2f_{i-1}+f_{i-2} ]

接下来,开始搬运式子。。。

(Min-Max)容斥的神奇应用

考虑到对于一个形如(f(i)=af(i-1)+bf(i-2))的式子,都有一个共同性质:

[gcd(f(a),f(b))=f(gcd(a,b)) ]

但问题在于这道题求的是(lcm)呀,这个性质怎么用呢?

因此,我们就要把(lcm)转化为(gcd),这里神奇地使用了(Min-Max)容斥:

[lcm(S)=prod_{Tsubseteq S,T ot=empty}gcd(T)^{(-1)^{|T|+1}} ]

显然,此处(gcd)对应(Min)(lcm)对应(Max)(相当于把因数看作集合),因此原本的(sum)变成了(prod),且((-1)^{|T|+1})这个系数就飞上去变成了次数。

然后我们就可以得到:

[g(n)=prod_{Tsubseteq S,T ot=empty}f(gcd(T))^{(-1)^{|T|+1}} ]

这个式子看起来还是很复杂啊,因此还需要继续推。

“最精髓的一步”(闪指导(hl666)评)

我也不知道是怎么想到的,反正这里我们需要构造一个函数(h(x))满足:(怎么构造之后会介绍)

[f(n)=prod_{d|n}h(d) ]

然后我们把这个东西代入原式,就得到了:

[g(n)=prod_{Tsubseteq S,T ot=empty}(prod_{d|gcd(T)}h(d))^{(-1)^{|T|+1}} ]

看到(d|n)这个东西,我们就不禁回想起莫比乌斯反演的基本套路,因此就会调整枚举顺序,改为枚举(d)

[g(n)=prod_{d=1}^nh(d)^{sum_{Tsubseteq S',T ot=empty}(-1)^{|T|+1}} ]

注意一下,此时的(S')表示({d,2d,3d,...,lfloorfrac nd floor imes d})这个集合。(实际上它是什么集合都无所谓

还有,原先的(prod),飞到上面去就变成了(sum)

然后我们仔细看看这个式子,就会发现,这个式子依旧很复杂,依旧不知道该怎么搞。

全体起立,思路清奇(闪指导(hl666)语)

让我们单独拎出此时的次数,如果我们去枚举(|T|),就会得到这样一个东西:

[sum_{i=1}^{|S'|}(-1)^{i+1}C_{|S|}^{i} ]

是不是立刻觉得有蹊跷?若我们把(i)改为从(0)开始枚举,那么就多出一项(-1),则我们在式子后面补上一个(1)使原式值不变得到:

[sum_{i=0}^{|S'|}(-1)^{i+1}C_{|S|}^i+1 ]

然后我们就会发现,(sum_{i=0}^{|S'|}(-1)^{i+1}C_{|S|}^i)实际上是等于(0)的!

于是我们就得到了:

[g(n)=prod_{i=1}^nh(i) ]

看着这个式子,尽管我们是一步一步有理有据推导过来的,是不是仍然有种不可置信的感觉?

莫比乌斯反演的神奇应用((h(x))的构造)

众所周知,莫比乌斯反演是长这样子的一个式子:

[F(n)=sum_{d|n}f(d)Leftrightarrow f(n)=sum_{d|n}mu(d) imes F(frac nd) ]

然后放在这题里,实际上我们有这样一个推论:

[f(n)=prod_{d|n}h(d)Leftrightarrow h(n)=prod_{d|n}f(frac nd)^{mu(d)} ]

由此我们可以发现,在这道题中很多我们曾熟悉,或自以为熟悉的公式,都因为(sum)变成了(prod),而变得如此陌生。

这也启发了我,学习一个式子真的需要深层地去了解,从而能更灵活的应用。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000000
using namespace std;
int n,X,f[N+5],h[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class LinearSieve//线性筛,筛莫比乌斯函数
{
	private:
		int Pt,P[N+5],mu[N+5];
	public:
		I int operator [] (CI x) Con {return mu[x];}
		I LinearSieve()
		{
			mu[1]=1;for(RI i=2,j;i<=N;++i)
				for(!P[i]&&(mu[P[++Pt]=i]=-1),j=1;j<=Pt&&i*P[j]<=N;++j)
					if(P[i*P[j]]=1,i%P[j]) mu[i*P[j]]=-mu[i];else break;
		}
}Mu;
int main()
{
	RI Tt,i,j,g,Inv,ans;scanf("%d",&Tt);W(Tt--)
	{
  		for(scanf("%d%d",&n,&X),i=1;i<=n;++i) h[i]=1;//初始化清空
		for(f[1]=1,i=2;i<=n;++i)//递推f,同时求出h
		{
			Inv=QP(f[i]=(2LL*f[i-1]+f[i-2])%X,X-2);//递推f,计算逆元
			for(j=1;i*j<=n;++j) Mu[j]&&(h[i*j]=1LL*h[i*j]*(~Mu[j]?f[i]:Inv)%X,0);//枚举倍数,更新h
		}
		for(ans=0,g=i=1;i<=n;++i) g=1LL*g*h[i]%X,ans=(1LL*i*g+ans)%X;//计算g,统计答案
		printf("%d
",ans);//输出答案
	}return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4833.html