CometOJ Contest #13 火鼠的皮衣 -不焦躁的内心
这是一道组合数学的巧妙转换的题目,极其简单,但是比赛的时候没有想出来。。嘤嘤嘤
其实也不简单,因为我不会矩阵快速幂
求下面这个公式对p取模的值,n,a,b,p均有 1e 18 的范围
∑
i
=
1
n
/
2
C
n
2
∗
i
a
i
b
n
−
2
∗
i
\sum_{i=1}^{n/2}C^{2*i}_{n}a^ib^{n-2*i}
i=1∑n/2Cn2∗iaibn−2∗i
首先这个,我们可以化为
∑
i
=
1
n
C
n
i
a
i
b
n
−
i
(
i
%
2
=
=
0
)
\sum_{i=1}^{n}C^{i}_{n}\sqrt{a}^ib^{n-i} (i\%2==0)
i=1∑nCniaibn−i(i%2==0)
哎呀,不想打了,这个公式好麻烦,直接上图
#include <bits/stdc++.h>
#define ll long long
#define sl __int128_t
using namespace std;
const int maxn=1e5+7;
int t;
ll n,a,b,p;
ll mul(ll x,ll y){
return sl(x)*sl(y)%sl(p);//注意还是会有可能爆longlong的
}
struct rect{//矩阵快速幂
ll num[3][3];
rect(){
memset(num,0,sizeof(num));
}
rect operator* (const rect xx) const{
rect tmp;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
tmp.num[i][j]=(tmp.num[i][j]%p+mul(this->num[i][k],xx.num[k][j]))%p;
return tmp;
}
};
rect poww(rect x,ll y){
rect ans;
for(int i=1;i<=n;i++)
ans.num[i][i]=1;
do{
if(y&1)
ans=ans*x;
x=x*x;
y>>=1;
}while(y>0);
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%lld %lld %lld %lld",&n,&a,&b,&p);//忘记p的范围也是
a%=p,b%=p;
ll A=(2*b)%p,B=(a+p-mul(b,b))%p;
rect g;
g.num[1][1]=A,g.num[1][2]=B,g.num[2][1]=1,g.num[2][2]=0;
g=poww(g,n-1);
ll zans=(mul(g.num[1][1],b)+g.num[1][2])%p;
printf("%lld\n",zans);
}
return 0;
}