[UER #6] 逃跑
一、题目
二、解法
以前的博客不是很好,现在另起炉灶重写一波。
首先我们知道 \(E(\overline x)=E(x^2)-E^2(x)\),这可以用期望的线性性简单推出。
可以先预处理 \(g(t,i,j)\),表示经过 \(t\) 的时间,我们走到 \((i,j)\) 的方案数,这个可以直接 \(O(n^3)\) 递推弄出来。对于 \(E(x)\) 我们考虑求单点的贡献,设 \(f(i)\) 表示经过 \(i\) 的时间第一次到达 \((a,b)\),对于所有 \((a,b)\) 的方案数求和:
设 \(pw(i)=(w_1+w_2+w_3+w_4)^i\),那么 \(E(x)\) 就可以很容易地求出了:
对于 \(E(x^2)\) 我们考虑求出点对之间的贡献,设 \(h(i,x,y)\) 表示在时间 \(i\) 内,我们经过 \((a,b)\),第一次到达 \((a+x,b+y)\) 的方案数。转移考虑容斥原理,总的方案数是先第一次走到 \((a,b)\),然后任意走到 \((a+x,b+y)\)
如果去重需要减去两项:第一项是经过 \((a+x,b+y)\) 走到 \((a,b)\),然后再走到 \((a+x,b+y)\);第二项是完成目标之后还在原地打转,也就是事实上不需要这么多时间,那么总转移方程:
因为我们是按经过顺序计算的贡献,所以如果对他求和求出来的是 \(E({x\choose 2})\),那么 \(E(x^2)\) 可以表示成:
三、总结
本题的转化链十分巧妙,经过的点数可以在第一次经过的时候算贡献。而第一次经过的模型是经典问题,我们常常正难则反,减去的东西就规约到了子问题。
状态定义也值得学习,本题是把一个等价类定义到了状态中。这是因为它们的总方案易于计算,而容斥的方式是本质相同的,最后的答案也只需要求和,所以可以压缩在一起,寻找等价类的关键就是找他们的共同点。
#include <cstdio>
const int M = 205;
const int N = 105;
const int MOD = 998244353;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,w1,w2,w3,w4,d1,d2;
int pw[M],f[M],g[M][M][M],h[M][M][M];
void add(int &x,int y) {x=((x+y)%MOD+MOD)%MOD;}
signed main()
{
n=read();w1=read();
w2=read();w3=read();w4=read();
g[0][0+N][0+N]=pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=pw[i-1]*(w1+w2+w3+w4)%MOD;
for(int t=1;t<=n;t++)
for(int i=-t;i<=t;i++)
for(int j=-t;j<=t;j++)
{
int tmp=g[t-1][i+N][j+N];
add(g[t][i+1+N][j+N],tmp*w1);
add(g[t][i-1+N][j+N],tmp*w2);
add(g[t][i+N][j+1+N],tmp*w3);
add(g[t][i+N][j-1+N],tmp*w4);
}
for(int i=0;i<=n;i++)
{
f[i]=pw[i];
for(int j=0;j<i;j++)
add(f[i],-f[j]*g[i-j][0+N][0+N]);
add(d1,f[i]*pw[n-i]);
}
for(int i=1;i<=n;i++)
for(int x=-i;x<=i;x++)
for(int y=-i;y<=i;y++)
{
if(x==0 && y==0) continue;
int &r=h[i][x+N][y+N];
for(int j=0;j<i;j++)
{
add(r,f[j]*g[i-j][x+N][y+N]);
add(r,-h[j][-x+N][-y+N]*g[i-j][x+N][y+N]);
add(r,-h[j][x+N][y+N]*g[i-j][0+N][0+N]);
}
add(d2,r*pw[n-i]);
}
add(d2,d2);add(d2,d1);
printf("%lld\n",((d2*pw[n]-d1*d1)%MOD+MOD)%MOD);
}