组合数学+二次剩余--51Nod 1236 序列求和 V3

传送门

n n n忘记取模调了半个小时祭。

f i b fib fib是有通项公式的,然后式子写出来就是这样:
x = 1 + 5 2 , y = 1 − 5 2 , z = 1 5 x=\frac{1+\sqrt{5}}{2},y=\frac{1-\sqrt{5}}{2},z=\frac{1}{\sqrt{5}} x=21+5 ,y=215 ,z=5 1
a n s = ∑ i = 1 n z k × ( x i − y i ) k ans=\sum_{i=1}^n z^k\times (x^i-y^i)^k ans=i=1nzk×(xiyi)k
进一步化简,用二项式定理展开,把后面无关的往前提,发现后面其实是一个等比数列求和,不断化简最终式子就变成:
a = x i × y k − i a=x^i\times y^{k-i} a=xi×yki
a n s = z k × ∑ i = 0 k ( − 1 ) k − i × C k i × a − a n + 1 1 − a ans=z^k\times \sum_{i=0}^k(-1)^{k-i}\times C_{k}^i\times \frac{a-a^{n+1}}{1-a} ans=zk×i=0k(1)ki×Cki×1aaan+1

注意要求一下 5 5 5的二次剩余,求解代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int mod=1e9+9;
int n=5,w;

inline int qpow(int x,int k){
	int ret=1;
	while(k){
		if(k&1) ret=1LL*ret*x%mod;
		x=1LL*x*x%mod; k>>=1;
	} return ret;
}

struct F{
	int x,y;
	F(){}
	F(const int &xx,const int &yy){x=xx,y=yy;}
	F operator *(const F &b) const{
		return F((1LL*x*b.x%mod+1LL*y*b.y%mod*w%mod)%mod,(1LL*y*b.x%mod+1LL*x*b.y%mod)%mod);
	}
};

inline F qpow(F x,int k){
	F ret(1,0);
	while(k){
		if(k&1) ret=ret*x;
		x=x*x; k>>=1;
	} return ret;
}

int main(){
	while(1){
		int a=1LL*rand()*rand()%mod;
		w=(1LL*a*a%mod-n+mod)%mod;
		if(qpow(w,(mod-1)>>1)==mod-1){
			F ans=qpow(F(a,1),(mod+1)>>1);
			cout<<ans.x<<endl; break;
		}
	}
}

最后 A C AC AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
#define maxn 100005
using namespace std;
const int sq5=616991993,mod=1e9+9;
int t,k,fac[maxn],inv[maxn],a[maxn],b[maxn];
LL n;

inline LL qpow(int x,LL k){
	LL ret=1;
	while(k){
		if(k&1LL) ret=1LL*ret*x%mod;
		x=1LL*x*x%mod; k>>=1;		
	} return ret;
}

inline LL C(int n,int m){ return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;}

int main(){
	scanf("%d",&t); fac[0]=1;
	for(int i=1;i<=100000;i++) fac[i]=1LL*fac[i-1]*i%mod;
	inv[100000]=qpow(fac[100000],mod-2);
	for(int i=100000;i;i--) inv[i-1]=1LL*inv[i]*i%mod;
	a[0]=b[0]=1;
	for(int i=1;i<=100000;i++) a[i]=1LL*a[i-1]*(1+sq5)%mod*inv[2]%mod,b[i]=1LL*b[i-1]*(1-sq5+mod)%mod*inv[2]%mod;
	while(t--){
		scanf("%lld%d",&n,&k);
		LL ans=0;
		for(int i=0;i<=k;i++){
			LL tmp=1LL*a[i]*b[k-i]%mod,res=0;
			if(tmp==1) res=C(k,i)*(n%mod)%mod;//!!!
			else res=C(k,i)*((qpow(tmp,(n+1)%(mod-1))-tmp+mod)%mod)%mod*qpow(tmp-1,mod-2)%mod;
			if(!((k-i)&1)) (ans+=res)%=mod;
			else (ans+=mod-res)%=mod;
		}
		ans=ans*qpow(qpow(sq5,mod-2),k)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值