把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【LOJ6485】LJJ 学二项式定理(单位根反演)

点此看题面

  • 给定\(n,s,a_{0\sim 3}\),求\(\sum_{i=0}^nC(n,i)\cdot s^i\cdot a_{i\%4}\)
  • 数据组数\(\le10^5,n\le10^{18}\)

单位根反演

关于单位根反演可以看一看这篇博客:单纯看懂公式的单位根反演

这题应该还算是比较板子的。

容易想到去枚举\(i\%4\)的余数\(k\),计算每个\(a_k\)的贡献,得到:

\[\sum_{k=0}^3a_k\sum_{i=0}^nC_n^i\cdot s^i\cdot[4|(i-k)] \]

直接上单位根反演的公式\([k|n]=\frac 1k\sum_{i=0}^{k-1}\omega_k^{ni}\)得到:

\[\frac14\sum_{k=0}^3a_k\sum_{i=0}^nC_n^i\cdot s^i\cdot\sum_{j=0}^3\omega_4^{j(i-k)} \]

我们提前\(j\)的枚举,并把\(\omega_4\)的指数\(j(i-k)\)拆开,得到:

\[\frac14\sum_{k=0}^3a_k\sum_{j=0}^3\omega_4^{-jk}\sum_{i=0}^nC_n^i\cdot s^i\times \omega_4^{ij} \]

对于最后一个\(\sum\)中的式子显然就是题目中明示的二项式定理,得到:

\[\frac14\sum_{k=0}^3a_k\sum_{j=0}^3\omega_4^{-jk}(s\cdot\omega_4^j+1)^n \]

后面的\((s\cdot\omega_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\n",1LL*t*I4%X);//最后记得给答案除以4
	}return 0;
}
posted @ 2021-03-26 11:24  TheLostWeak  阅读(90)  评论(0编辑  收藏  举报