P8558 黑暗(Darkness)
NaCly_Fish · · 题解
可以先考虑计算从
建立生成函数,设
多出来的这个 +1 就是因为要补上
提取系数即得
那么计算从
现在只需要枚举墙边的位置,根据走到那撞到墙的概率,就可以将答案表示为:
你会发现有些位置可能被重复计算了,但没关系。被计了两次的位置,都是有两个方向面对墙的位置。
这里就说
后面那个和式可以单独计算,先化简为
这种超几何式显然可以整式递推计算,利用二项式系数的递推公式:
可以在线性时间内递推计算,最后用线性筛来求
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 10000003
#define ll long long
#define p 998244353
using namespace std;
inline int power(int a,int t){
int res = 1;
while(t){
if(t&1) res = (ll)res*a%p;
a = (ll)a*a%p;
t >>= 1;
}
return res;
}
const int inv3 = 332748118;
int fac[N],ifac[N],pw[N],pr[N>>3],inv[N];
int cnt;
void init(int n,int k){
inv[1] = pw[1] = fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for(int i=2;i<=n;++i) fac[i] = (ll)fac[i-1]*i%p;
ifac[n] = power(fac[n],p-2);
for(int i=n-1;i;--i) ifac[i] = (ll)ifac[i+1]*(i+1)%p;
for(int i=2;i<=n;++i){
inv[i] = (ll)fac[i-1]*ifac[i]%p;
if(!pw[i]){
pr[++cnt] = i;
pw[i] = power(i,k);
}
for(int j=1;j<=cnt&&i*pr[j]<=n;++j){
pw[i*pr[j]] = (ll)pw[i]*pw[pr[j]]%p;
if(i%pr[j]==0) break;
}
}
}
int g(int A,int B,int C,int k){
if(A>B) swap(A,B);
ll res = 0;
int bc = 1,f = 1,ifa = ifac[A],ifb = ifac[B],ipw3 = 1;
for(int i=0;i<=A+B;++i){
res += (ll)pw[A+B-i]*f%p*ipw3%p*bc%p;
ipw3 = ipw3*332748118ll%p;
bc = (ll)bc*(i+C+1)%p*inv[i+1]%p;
if(i<A) f = (f<<1)>=p?(f<<1)-p:f<<1;
else if(i<B) f = ((f<<1)-(ll)fac[i]*ifa%p*ifac[i-A])%p;
else f = ((f<<1)-((ll)ifa*ifac[i-A]+(ll)ifb*ifac[i-B])%p*fac[i])%p;
}
return res%p*power(3,p-C-1)%p;
}
int A,B,C,k;
int main(){
scanf("%d%d%d%d",&A,&B,&C,&k);
init(A+B+C-min(min(A,B),C),k);
int ans = (ll)((g(A,B,C,k)+g(A,C,B,k))%p+g(B,C,A,k))*inv3%p;
printf("%d",(ans+p)%p);
return 0;
}