JZOJ6077 K君的游戏
book
归档:
solution
flag
⭐
Problem
一个博弈游戏:给定一棵有根树,放一个石子在根节点,轮流执手,每次往任意叶子节点方向移一步,不能移则为输。
现在给你 $n$ 个点,生成任意一棵有根树,问先手获胜的概率。有 $q$ 次询问。
$n,q \leq 10^5$
Solution
令 $f(x), g(x)$ 分别为后手和先手必胜的 EGF ,则 $f(x)+g(x)=\sum \frac{(i-1)!}{i!}x^i$ 。所以有 $g(x)=-ln(1-x)-f(x)$ 。其中 $(i-1)!$ 为无标号有根树的方案数,$\sum \frac{(-1)^{i+1}}{i}x^i$ 为 $ln(x+1)$ 的麦克劳林级数。
根据SG函数,因为后手必胜的子树皆为先手必胜,那么有 $xe^{g(x)}=f(x)$ 。解方程得 $f(x)=ln(1-ln(1-x))$ 。并不知道过程。
所以可以得到最终的概率:$\frac{(n-1)!-n!f_n}{(n-1)!}=1-nf_n$ 。
Code
#include<bits/stdc++.h>
#define MOD (998244353)
using namespace std;
const int N=4e5+10;
int f[N], g[N], h[N], r[N], n;
void gen(int m) {
int l=0;
for(n=1; n<=m; n<<=1) l++;
for(int i=1; i<n; i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
int qpow(int x, int y) {
assert(y>=0);
int ret=1;
while(y) {
if(y&1) ret=1ll*ret*x%MOD;
x=1ll*x*x%MOD, y>>=1;
}
return ret;
}
void dft(int x[], int d) {
for(int i=1; i<n; i++) if(r[i]>i) swap(x[i], x[r[i]]);
int t, w, o;
for(int i=1; i<n; i<<=1) {
o=qpow(3, d*(MOD-1)/(i<<1)+MOD-1);
for(int j=0; j<n; j+=(i<<1)) {
w=1;
for(int k=0; k<i; k++, w=1ll*w*o%MOD)
t=1ll*x[i+j+k]*w%MOD, x[i+j+k]=(x[j+k]-t+MOD)%MOD,
(x[j+k]+=t)%=MOD;
}
}
}
void mul(int x[], int y[], int z[], int m, int mode=0) {
gen(m<<1);
int a[N]={0}, b[N]={0};
copy(x, x+m, a), copy(y, y+m, b);
dft(a, 1), dft(b, 1);
for(int i=0; i<n; i++)
if(!mode) a[i]=1ll*a[i]*b[i]%MOD;
else a[i]=(2ll*b[i]-1ll*a[i]*b[i]%MOD*b[i]%MOD+MOD)%MOD;
dft(a, -1);
int inv=qpow(n, MOD-2); for(int i=0; i<m; i++) z[i]=1ll*a[i]*inv%MOD;
}
void inv(int a[], int f[], int deg) {
assert(deg>0);
if(deg==1) { f[0]=qpow(a[0]%MOD, MOD-2); return; }
inv(a, f, (deg+1)>>1);
mul(a, f, f, deg, 1);
}
void deri(int x[], int n) {
for(int i=0; i<n; i++) x[i]=1ll*(i+1)*x[i+1]%MOD;
x[n-1]=0;
}
void inte(int x[], int n) {
for(int i=n-1; i>0; i--) x[i]=1ll*x[i-1]*qpow(i, MOD-2)%MOD;
x[0]=0;
}
void pln(int x[], int y[], int n) {
int a[N]={0}, b[N]={0}; copy(x, x+n, a);
inv(a, b, n);
deri(a, n);
mul(a, b, a, n);
inte(a, n);
for(int i=0; i<n; i++) y[i]=a[i];
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int n=1e5+10;
f[0]=1, f[1]=MOD-1;
pln(f, g, n);
for(int i=0; i<n; i++) g[i]=(MOD-g[i])%MOD;
g[0]++;
pln(g, h, n);
int q;
cin>>q;
while(q--) {
int na; cin>>na;
cout<<(1-1ll*na*h[na]%MOD+MOD)%MOD<<'\n';
}
return 0;
}
navigate_before
JZOJ6080 IOer
SPOJ1812 LCS2
navigate_next