计蒜客决赛Day2 T2 类斐波拉契数据分析

Description

Solution

  • 关键在于发现结论,以及简单的反演。

  • fib数列: f n = k f n − 1 + f n − 2 f_n=kf_{n-1}+f_{n-2} fn=kfn1+fn2满足 f x + y = f x − 1 f y + f x f y + 1 f_{x+y}=f_{x-1}f_y+f_xf_{y+1} fx+y=fx1fy+fxfy+1,转移矩阵为 A n = [ [ f n − 1 , f n ] , [ f n , f n + 1 ] ] A^n=[[f_{n-1},f_n],[f_{n},f_{n+1}]] An=[[fn1,fn],[fn,fn+1]] ,因此 g c d ( f i , f j ) = f g c d ( i , j ) gcd(f_i,f_j)=f_{gcd(i,j)} gcd(fi,fj)=fgcd(i,j)

  • 推一推可以写出 ∑ i ∑ k ∣ i ∑ j ≤ i [ ( i , j ) = 1 ] [ j % k = − d ] \sum_i\sum_{k|i}\sum_{j\le i}[(i,j)=1][j\%k=-d] ikiji[(i,j)=1][j%k=d]

  • 不妨设 d > 0 d>0 d>0,那么必须有 ( k , j ) = 1 (k,j)=1 (k,j)=1

  • 有结论, [ k ∣ i ] ∑ j ≤ i [ ( i , j ) = 1 ] [ ( k , j ) = 1 ] [ j % k = d ] [k|i]\sum_{j\le i}[(i,j)=1][(k,j)=1][j\%k=d] [ki]ji[(i,j)=1][(k,j)=1][j%k=d]对于所有 [ ( d , k ) = 1 ] [(d,k)=1] [(d,k)=1]都相等,即 j j j % k \%k %k意义下是均匀分布的。

  • 对于一类与整除相关的问题,联想到 ϕ ( n ) \phi(n) ϕ(n)的求法,我们可以类似的将 n n n拆成若干质因数的次幂考虑。

  • 考虑对于某个 p p p k = p x , i = p x + y k=p^x,i=p^{x+y} k=px,i=px+y,考虑 j j j m o d    p x + y \mod p^{x+y} modpx+y下要同时满足上面的两个互质性质,然后再通过CRT合并,那么只需要证明在这个意义下是均匀的即可,那么这个就很显然是对的了。

  • 后面的都很简单了。

  • 两个小Tips:1.按照质因数次幂分别考虑,再合并。2.统计 f g c d f_{gcd} fgcd可以将贡献统计到 k ∣ g c d , h k k|gcd,h_k kgcd,hk的位置上,用反演可以简单求出 h h h

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxn 1000005
#define ll long long
using namespace std;

int T,n,K,d,mo,i,j,k,c[maxn];
ll f[maxn],g[maxn];

int pri[maxn],tot,phi[maxn],bz[maxn],minp[maxn],mu[maxn];
void getpri(){
	phi[1]=minp[1]=mu[1]=1;
	for(i=2;i<maxn;i++){
		if (!bz[i]) pri[++tot]=i,phi[i]=i-1,minp[i]=i,mu[i]=-1;
		for(j=1;j<=tot&&i*pri[j]<maxn;j++){
			bz[i*pri[j]]=1,minp[i*pri[j]]=pri[j];
			if (i%pri[j]==0) {
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			} else phi[i*pri[j]]=phi[i]*(pri[j]-1),mu[i*pri[j]]=-mu[i];
		}
	}
}

int main(){
	freopen("ceshi.in","r",stdin);
//	freopen("analyze.in","r",stdin);
//	freopen("analyze.out","w",stdout);
	scanf("%d",&T);
	getpri();
	while (T--){
		scanf("%d%d%d%d",&n,&d,&K,&mo);
		for(i=2,f[1]=1;i<=n;i++) f[i]=(f[i-1]*K+f[i-2])%mo;
		if (!d){
			ll cnt=0;
			for(i=1;i<=n;i++) cnt+=phi[i];
			printf("%lld\n",cnt%mo*f[1]%mo);
			continue;
		}
		memset(g,0,sizeof(ll)*(n+1));
		for(i=1;i<=n;i++) for(j=1;i*j<=n;j++) if (mu[j])
			g[i*j]+=f[i]*mu[j];
		for(i=1;i<=n;i++) g[i]%=mo;
		c[1]=1;
		for(i=2;i<=n;i++) c[i]=c[i/minp[i]]&(d%minp[i]>0);
		ll ans=0;
		for(i=1;i<=n;i++) if (c[i]){
			ll sum=0;
			for(j=i;j<=n;j+=i) sum+=phi[j];
			(ans+=sum/phi[i]%mo*g[i])%=mo;
		}
		printf("%lld\n",(ans%mo+mo)%mo);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值