题解:回家
1. Description
现在有一个人离家 \(n\) 步,每一次有 \(\frac 12\) 的概率靠近家一步,有 \(\frac 12\) 的概率远离家一步,求最多走 \(k\) 步回家的概率。
2. Solution
写一下赛时的神秘做法。
首先不难想到一个简单 dp,定义 \(f_i\) 表示消耗了 \(i\) 点体力回家的方案数,那么答案就是 \(\sum_{i=n}^k \frac{f_i}{2^i}\)。
现在考虑怎么求 \(f_i\),首先不难想到,消耗了 \(i\) 点体力回家,需要靠近家 \(\frac{i+n}{2}\) 步,方案数是 \(C_{i+n}^{\frac{i+n}{2}}\),但是会出现还没有消耗完体力提前到家的情况,消耗了 \(j\) 点体力到家,那么有 \(i-j\) 步多余,这 \(i-j\) 步中,有 \(\frac{i-j}{2}\) 步都是靠近家的,所以会重复算 \(C_{i-j}^{\frac{i-j}{2}}\) 次,因此 \(f_i=C_{i+n}^{\frac{i+n}{2}}-\sum_{j=n}^{i-1}f_jC_{i-j}^{\frac{i-j}{2}}\)。不难得到 \(O(n^2)\) 的 dp。
考虑如何优化,显然有 \(f_{n}=1,f_{n+2}=n\),然后我们考虑带入递推式就会发现一个很有意思的规律,\(f_{n+2k}=\frac{n\times (n+2k-1)!}{(n+k)!\times k!}\),这样可以 \(O(1)\) 计算。
实际上,\(f_{n+2k}=\frac{nC_{n+2k}^{k}}{n+2k}\),可以应用卡特兰数的相关知识求解。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int mod=998244353,N=5e6+5;
int n,k;
int fac[N],inv[N];
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int binpow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
b>>=1;
a=mul(a,a);
}
return res;
}
int C(int m,int n){
return mul(fac[n],mul(inv[n-m],inv[m]));
}
signed main(){
read(n),read(k);
if(n>k){
puts("0");
exit(0);
}
fac[0]=1;
for(int i=1;i<=k;i++)
fac[i]=mul(fac[i-1],i);
inv[k]=binpow(fac[k],mod-2);
for(int i=k-1;i>=0;i--)
inv[i]=mul(inv[i+1],i+1);
int K=mul(inv[2],inv[2]);
int ans=0;
for(int i=n,las=n,M=n-1,pw=binpow(inv[2],n),res;i<=k;i+=2,M+=2,las++,pw=mul(pw,K)){
res=mul(n,mul(fac[M],mul(inv[las],inv[(i-n)/2])));
ans=add(ans,mul(res,pw));
}
write(ans),Nxt;
}
```## 1. Description
现在有一个人离家 $n$ 步,每一次有 $\frac 12$ 的概率靠近家一步,有 $\frac 12$ 的概率远离家一步,求最多走 $k$ 步回家的概率。
## 2. Solution
写一下赛时的神秘做法。
首先不难想到一个简单 dp,定义 $f_i$ 表示消耗了 $i$ 点体力回家的方案数,那么答案就是 $\sum_{i=n}^k \frac{f_i}{2^i}$。
现在考虑怎么求 $f_i$,首先不难想到,消耗了 $i$ 点体力回家,需要靠近家 $\frac{i+n}{2}$ 步,方案数是 $C_{i+n}^{\frac{i+n}{2}}$,但是会出现还没有消耗完体力提前到家的情况,消耗了 $j$ 点体力到家,那么有 $i-j$ 步多余,这 $i-j$ 步中,有 $\frac{i-j}{2}$ 步都是靠近家的,所以会重复算 $C_{i-j}^{\frac{i-j}{2}}$ 次,因此 $f_i=C_{i+n}^{\frac{i+n}{2}}-\sum_{j=n}^{i-1}f_jC_{i-j}^{\frac{i-j}{2}}$。不难得到 $O(n^2)$ 的 dp。
考虑如何优化,显然有 $f_{n}=1,f_{n+2}=n$,然后我们考虑带入递推式就会发现一个很有意思的规律,$f_{n+2k}=\frac{n\times (n+2k-1)!}{(n+k)!\times k!}$,这样可以 $O(1)$ 计算。
实际上,$f_{n+2k}=\frac{nC_{n+2k}^{k}}{n+2k}$,可以应用卡特兰数的相关知识求解。
## 3. Code
```c++
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int mod=998244353,N=5e6+5;
int n,k;
int fac[N],inv[N];
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int binpow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
b>>=1;
a=mul(a,a);
}
return res;
}
int C(int m,int n){
return mul(fac[n],mul(inv[n-m],inv[m]));
}
signed main(){
read(n),read(k);
if(n>k){
puts("0");
exit(0);
}
fac[0]=1;
for(int i=1;i<=k;i++)
fac[i]=mul(fac[i-1],i);
inv[k]=binpow(fac[k],mod-2);
for(int i=k-1;i>=0;i--)
inv[i]=mul(inv[i+1],i+1);
int K=mul(inv[2],inv[2]);
int ans=0;
for(int i=n,las=n,M=n-1,pw=binpow(inv[2],n),res;i<=k;i+=2,M+=2,las++,pw=mul(pw,K)){
res=mul(n,mul(fac[M],mul(inv[las],inv[(i-n)/2])));
ans=add(ans,mul(res,pw));
}
write(ans),Nxt;
}

浙公网安备 33010602011771号