「洛谷P5591」小猪佩奇学数学

解锁新姿势。

传送门

洛谷P5591

题解

如果你会NTT或者FFT,那么要理解起来就会比较容易。

首先来看下单位根反演的式子:

证明还是比较简单的,分类讨论一下:

  1. 如果$k\mid n$,那么

  2. 如果$k\nmid n$,那么

本题中模数为$998244353$,原根为$3$,用这个来代就好了。

然后让我们来推题目里的式子:

先推括号里的前半部分,把乘$i$弄到组合数里,再用二项式定理把式子化为幂的形式:

再来推后半部分,套上单位根反演的式子:

式子的最后一部分就是一个等差数列乘以等比数列,随便推一下$O(1)$求和的式子就好了,注意分母不能为0要特判。

代码

代码还是挺短的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<cstdio>
using namespace std;
typedef long long LL;
const int TT=998244353;
int n,p,k,ans,gk;
inline int QP(int a,int b)
{
int ret=1,w=a;
while(b)
{
if(b&1) ret=(LL)ret*w%TT;
w=(LL)w*w%TT;b>>=1;
}
return ret;
}
inline void Inc(int& x,int y){x=(x+y)%TT;}
inline void Dec(int& x,int y){x=(x-y+TT)%TT;}
inline int S(int r)
{
if(r==1) return (LL)k*(k-1)/2%TT;
int inv=QP(r-1,TT-2);
return ((LL)k*QP(r,k)%TT*inv%TT-(LL)(QP(r,k+1)-r+TT)%TT*inv%TT*inv%TT+TT)%TT;
}
int main()
{
scanf("%d%d%d",&n,&p,&k);
gk=QP(3,(TT-1)/k);
Inc(ans,(LL)n*p%TT*QP(p+1,n-1)%TT);
for(int j=0;j<k;j++)
{
int gkj=QP(gk,j);
Dec(ans,(LL)QP(k,TT-2)*QP(((LL)p*gkj+1)%TT,n)%TT*S(QP(gkj,TT-2))%TT);
}
ans=(LL)ans*QP(k,TT-2)%TT;
printf("%d\n",ans);
return 0;
}