4833: [Lydsy1704月赛]最小公倍佩尔数
Time Limit: 8 Sec Memory Limit: 128 MBSubmit: 202 Solved: 99
[Submit][Status][Discuss]
Description
令(1+sqrt(2))^n=e(n)+f(n)*sqrt(2),其中e(n),f(n)都是整数,显然有(1-sqrt(2))^n=e(n)-f(n)*sqrt(2)。令g(
n)表示f(1),f(2)…f(n)的最小公倍数,给定两个正整数n和p,其中p是质数,并且保证f(1),f(2)…f(n)在模p意义
下均不为0,请计算sigma(i*g(i)),1<=i<=n.其在模p的值。
Input
第一行包含一个正整数 T ,表示有 T 组数据,满足 T≤210 。接下来是测试数据。每组测试数据只占一行,包含
两个正整数 n 和 p ,满足 1≤n≤10^6,2≤p≤10^9+7 。保证所有测试数据的 n 之和不超过 3×10^6 。
Output
对于每组测试数据,输出一行一个非负整数,表示这组数据的答案。
Sample Input
5
1 233
2 233
3 233
4 233
5 233
1 233
2 233
3 233
4 233
5 233
Sample Output
1
5
35
42
121
5
35
42
121
HINT
Source
一眼看去就是一道毒瘤数论题233333
首先要把 f() 这个数列搞出来,把f(n)的表达式写出来,发现是 ∑ [i%2==1] * C(n,i) * (sqrt(2)^(i-1))。
(打个表就可以发现 f(n) = 2 * f(n-1) + f(n-2) 了嘛 2333)
显然类菲波那切数列(满足 f(n+m) = f(n+1) * f(m) + f(n) * f(m-1)) 都是满足 gcd(f(x) , f(y)) = f( gcd(x,y) )的啊,之后对指数做min_max容斥,就可以得到一坨子关于子集gcd乘乘除除的玩意,看起来还有点麻烦。。。
于是可以再设 f(n) = π h(d) * [d|n] ,然后样把所有 f(gcd) 替换成 π h(),化简一下就可以发现 : g(i) = π h(j) * [j<=i] ,于是就可以直接做了QWQ
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1e6+5; inline int add(int x,int y,const int ha){ x+=y; return x>=ha?x-ha:x;} inline void ADD(int &x,int y,const int ha){ x+=y; if(x>=ha) x-=ha;} inline int mul(int x,int y,const int ha){ return x*(ll)y%ha;} inline int ksm(int x,int y,const int ha){ int an=1; for(;y;y>>=1,x=mul(x,x,ha)) if(y&1) an=mul(an,x,ha); return an; } int T,f[maxn],h[maxn],n,ans=0,p,now; inline void solve(){ f[1]=1; for(int i=2;i<=n;i++) f[i]=add(add(f[i-1],f[i-1],p),f[i-2],p); for(int i=1,inv;i<=n;i++){ h[i]=f[i],inv=ksm(h[i],p-2,p); for(int j=i*2;j<=n;j+=i) f[j]=mul(f[j],inv,p); now=mul(now,h[i],p); ADD(ans,mul(now,i,p),p); } } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&p); ans=0,now=1,solve(); printf("%d ",ans); } return 0; }