Atcoder ABC232-E Rook Path

Atcoder ABC232-E Rook Path

题目大意

有一个 H ∗ W H*W HW的表格, 在表格中有个车, 它开始在 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)的位置,它可以移动到同一个行或同一列的其他格子,但不能原地不动。 现在,请问移动 K K K次,它恰好出现在 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的方案数。 答案可能很大,你需要 ( m o d 998244353 ) (mod998244353) (mod998244353)

思路

比赛看题表忙忙,赛后题解脑茫茫,直到上问Depseaspary才取得真经。

这题还是老熟人dp

转移方程

我们设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示 移动 k k k次到达 第 i i i行 第 j j j列 的方案数,于是很容易想到 k − 1 k-1 k1的第 i i i行加上的 j j j行的和减去2倍(加行算一次,加列算一次)自己(不可以停留不动),即:

Atcoder ABC232-E Rook Path_第1张图片
f [ i ] [ j ] [ k ] = ∑ i = 1 H f [ i ] [ j ] [ k − 1 ] + ∑ j = 1 W f [ i ] [ j ] [ k − 1 ] − 2 ∗ f [ i ] [ j ] [ k − 1 ] f[i][j][k]={\sum_{i=1}^H f[i][j][k-1]}+{\sum_{j=1}^W}f[i][j][k-1]-2*f[i][j][k-1] f[i][j][k]=i=1Hf[i][j][k1]+j=1Wf[i][j][k1]2f[i][j][k1]
我们再设 h [ i ] [ k ] h[i][k] h[i][k]为 第 k k k次移动到达第 i i i行的方案数的和 , w [ j ] [ k ] w[j][k] w[j][k]为 第 k k k次移动到达第 j j j列的方案数的和。
f [ i ] [ j ] [ k ] = h [ i ] [ k − 1 ] + w [ j ] [ k − 1 ] − 2 ∗ f [ i ] [ j ] [ k − 1 ] f[i][j][k]=h[i][k-1]+w[j][k-1]-2*f[i][j][k-1] f[i][j][k]=h[i][k1]+w[j][k1]2f[i][j][k1]

A [ k ] A[k] A[k] IOI 表示步数为 k k k时的表格中方案数的和。

如图:

Atcoder ABC232-E Rook Path_第2张图片

  • i i i行的每一个格子 ( i , j ) (i,j) (i,j) i i i是固定的, j j j会变动)的方案数都为 h [ i ] [ k − 1 ] h[i][k-1] h[i][k1] w [ j ] [ k − 1 ] w[j][k-1] w[j][k1],图中的紫线加上所在列的蓝线的和。
  • 由于 j j j W W W个位置可以取,而 ∑ j = 1 W w [ j ] [ k − 1 ] \sum_{j=1}^W w[j][k-1] j=1Ww[j][k1]就是整个表格在 k − 1 k-1 k1步时的方案数的和,即 A [ k − 1 ] A[k-1] A[k1]
  • 由于 j j j W W W个位置可以取,所以 h [ i ] [ k − 1 ] h[i][k-1] h[i][k1]会被加 W W W次,即 h [ i ] [ k − 1 ] ∗ W h[i][k-1]*W h[i][k1]W.
  • 因为不可以不走动,所以应该减去停留不动的情况。又 i i i行被加两次( A [ k − 1 ] A[k-1] A[k1] 和 每一个 i i i行格子自己相加( = h [ i ] [ k − 1 ] =h[i][k-1] =h[i][k1]))即 2 ∗ h [ i ] [ k − 1 ] 2*h[i][k-1] 2h[i][k1]

h [ i ] [ k ] = h [ i ] [ k − 1 ] ∗ W + A [ k − 1 ] − 2 ∗ h [ i ] [ k − 1 ] = ( W − 2 ) ∗ h [ i ] [ k − 1 ] + A [ k − 1 ] h[i][k]=h[i][k-1]*W+A[k-1]-2*h[i][k-1]=(W-2)*h[i][k-1]+A[k-1] h[i][k]=h[i][k1]W+A[k1]2h[i][k1]=(W2)h[i][k1]+A[k1]

w [ j ] [ k ] w[j][k] w[j][k]也可以如上推理表示。
w [ j ] [ k ] = w [ j ] [ k − 1 ] ∗ H + A [ k − 1 ] − 2 ∗ W [ j ] [ k − 1 ] = ( H − 2 ) ∗ w [ j ] [ k − 1 ] + A [ k − 1 ] w[j][k]=w[j][k-1]*H+A[k-1]-2*W[j][k-1]=(H-2)*w[j][k-1]+A[k-1] w[j][k]=w[j][k1]H+A[k1]2W[j][k1]=(H2)w[j][k1]+A[k1]

现在我们发现只有 A [ k ] A[k] A[k]这个小淘气还没有被宝表示出来,那我们也给 A [ k ] A[k] A[k]量身定制一套表示方案

对与 A [ k ] A[k] A[k]中每一个点 ( i , j ) (i,j) (i,j),都是由其 k − 1 k-1 k1步时的行加列所得,那么每一行会被加 W W W次,每一列会被加 H H H次,那么每一个点就会加 H + W H+W H+W次 。

那么 A [ k ] = A [ k − 1 ] ∗ ( H + W ) A[k]=A[k-1]*(H+W) A[k]=A[k1](H+W)

因为不可以走动的老规则,所以答案应该 − 2 -2 2
A [ k ] = A [ k − 1 ] ∗ ( H + W − 2 ) A[k]=A[k-1]*(H+W-2) A[k]=A[k1](H+W2)

状态转移方程总结

当第 k k k步时

( i , j ) (i,j) (i,j)方案数
f [ i ] [ j ] [ k ] = h [ i ] [ k − 1 ] + w [ j ] [ k − 1 ] − 2 ∗ f [ i ] [ j ] [ k − 1 ] f[i][j][k]=h[i][k-1]+w[j][k-1]-2*f[i][j][k-1] f[i][j][k]=h[i][k1]+w[j][k1]2f[i][j][k1]
i i i行的方案数
h [ i ] [ k ] = ( W − 2 ) ∗ h [ i ] [ k − 1 ] + A [ k − 1 ] h[i][k]=(W-2)*h[i][k-1]+A[k-1] h[i][k]=(W2)h[i][k1]+A[k1]
j j j列的方案数
w [ j ] [ k ] = ( H − 2 ) ∗ w [ j ] [ k − 1 ] + A [ k − 1 ] w[j][k]=(H-2)*w[j][k-1]+A[k-1] w[j][k]=(H2)w[j][k1]+A[k1]
全图总方案数
A [ k ] = A [ k − 1 ] ∗ ( H + W − 2 ) A[k]=A[k-1]*(H+W-2) A[k]=A[k1](H+W2)

初始值

A [ 0 ] = 1 A[0]=1 A[0]=1

第0步时,只有 ( x 1 , y 1 ) (x1,y1) (x1,y1)出生点可以到达

如果 x 1 = = x 2 x1==x2 x1==x2 那么 h [ x 1 ] [ 0 ] = 1 h[x1][0]=1 h[x1][0]=1

x 1 ( x 2 ) x1(x2) x1(x2)行在没走的时候就可以到达

如果 y 1 = = y 2 y1==y2 y1==y2 那么 w [ y 1 ] [ 0 ] = 1 w[y1][0]=1 w[y1][0]=1

y 1 ( y 2 ) y1(y2) y1(y2)列在没走的时候就可以到达

优化

如果你看数据范围的话,你会有手撕出题人的想法……

Atcoder ABC232-E Rook Path_第3张图片

难不成我们手推这么久的光阴就要浪费了吗?

为让Depseaspary开心点老实点,我们先观察方程。

不难发现,方程中的行号和列号根本没变!

也就是说,我们没必要去开行和列的数组,更不用枚举行和列,直接求和不就完了!

CODE

#include
using namespace std;

#define ll long long

const int maxn=2e6+5,mod=998244353;

ll n,m,k,sx,sy,ex,ey;
ll f[maxn],h[maxn],w[maxn],a[maxn];

int main()
{
    scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&m,&k,&sx,&sy,&ex,&ey);
    a[0]=1;
    for(int i=1;i<=k;i++) a[i]=(a[i-1]*((n+m-2)%mod))%mod;

    if(sx==ex) h[0]=1;
    if(sy==ey) w[0]=1;
    if(sx==ex&&sy==ey) f[0]=1;
    for(int i=1;i<=k;i++) h[i]=((h[i-1]*((m-2)%mod))%mod+a[i-1])%mod;
    for(int i=1;i<=k;i++) w[i]=((w[i-1]*((n-2)%mod))%mod+a[i-1])%mod;

    for(int i=1;i<=k;i++) f[i]=(((h[i-1]+w[i-1])%mod)-(2*f[i-1]%mod))%mod;

    printf("%lld",(f[k]+mod)%mod);//防止负数
}

在最后的最后,我愿意说一句:“Depseaspary Orz”。

你可能感兴趣的:(atcoder题解,c++,算法,动态规划)