- 给定(n,s,a_{0sim 3}),求(sum_{i=0}^nC(n,i)cdot s^icdot a_{i\%4})。
- 数据组数(le10^5,nle10^{18})
单位根反演
关于单位根反演可以看一看这篇博客:单纯看懂公式的单位根反演。
这题应该还算是比较板子的。
容易想到去枚举(i\%4)的余数(k),计算每个(a_k)的贡献,得到:
[sum_{k=0}^3a_ksum_{i=0}^nC_n^icdot s^icdot[4|(i-k)]
]
直接上单位根反演的公式([k|n]=frac 1ksum_{i=0}^{k-1}omega_k^{ni})得到:
[frac14sum_{k=0}^3a_ksum_{i=0}^nC_n^icdot s^icdotsum_{j=0}^3omega_4^{j(i-k)}
]
我们提前(j)的枚举,并把(omega_4)的指数(j(i-k))拆开,得到:
[frac14sum_{k=0}^3a_ksum_{j=0}^3omega_4^{-jk}sum_{i=0}^nC_n^icdot s^i imes omega_4^{ij}
]
对于最后一个(sum)中的式子显然就是题目中明示的二项式定理,得到:
[frac14sum_{k=0}^3a_ksum_{j=0}^3omega_4^{-jk}(scdotomega_4^j+1)^n
]
后面的((scdotomega_4^j+1)^n)只与(j)有关可以先预处理,这样就不用做(16)次快速幂,而是只做(4)次,尽管这点常数优化没啥意义。
代码:(O(4Tlogn))
#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 X 998244353
using namespace std;
long long n;int s,w[5],v[4];
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;}
int main()
{
RI Tt,i,j,x;for(w[0]=1,w[1]=QP(3,(X-1)/4),i=2;i<=4;++i) w[i]=1LL*w[i-1]*w[1]%X;//预处理单位根(方便起见预处理到4次)
RI t=0,I4=QP(4,X-2);scanf("%d",&Tt);W(Tt--)
{
for(scanf("%lld%d",&n,&s),t=i=0;i^4;++i) v[i]=QP((1LL*s*w[i]+1)%X,n%(X-1));//计算(s·w^i+1)^n
for(i=0;i^4;++i) for(scanf("%d",&x),j=0;j^4;++j) t=(1LL*w[4-i*j%4]*v[j]%X*x+t)%X;//枚举每种余数计算贡献
printf("%d
",1LL*t*I4%X);//最后记得给答案除以4
}return 0;
}