AtCoder Beginner Contest 232 E 线性DP 组合数学

71 篇文章 1 订阅
54 篇文章 1 订阅

题目

有一个H*M的棋盘,起初小车在x1 , y1 ,小车要到达x2 , y2 。
小车每次操作只能到达棋盘中同行或者同列的某个位置。
要求计算小车从起点到达终点,并且恰好使用K次操作的方法数。
答案对998244353取余。

题解思路

这种问题可以先想想将行列分开考虑。
我们可以根据最后一个不同点来划分,即小车到没到达目标的行或列。
以横坐标为例

dp[ i ] [ 0 ] 表示小车走了 i 步 走完后不在 目标的列 ,1 表示在目标的列 的所有合法方案 。
根据这个条件 , 很容易得出转移方程。

        dx[i][1] = dx[i-1][0]%mod ; 
        dx[i][0] = dx[i-1][1]*(h-1)%mod + dx[i-1][0]*(h-2)%mod ;

最后能到达终点的必然是 x y 都到达了终点 , 即他们的状态都是 1 ,
我们进行组合即可。
现在走了dx[ i ][ 1 ] * dy[ k - i ][ 1 ] 。
现在知道要走分别要几步的方案了。
但是对x y 步数的顺序 还得进行组合。
即从k步中选择i步来走x。
这里就用到了组合数。
这样就得用到逆元了。

AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long

using namespace std;

const  int  INF =  0x3f3f3f3f;
const  int  N =  1000100;
const  int mod = 998244353 ; 

long long  dx[N][3] ; 
long long  dy[N][3] ; 
long long  jc[N] ; 

long long ksm(long long di , long long mi )
{
    long long res = 1 ; 
    while ( mi )
    {
        if ( mi & 1 )
            res = res*di %mod ; 
        di = di*di%mod ; 
        mi >>= 1 ; 
    }
    return res ; 
}
long long zuhe(int n , int m )
{
    return jc[n]*ksm(jc[n-m],mod-2)%mod*ksm(jc[m],mod-2)%mod ;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int h , w , k ;
    cin >> h >> w >> k ;
    int x1 , x2 , y1 , y2 ;
    cin >> x1 >> y1 >> x2 >> y2 ;
    jc[0] = 1 ; 
    for (int i = 1 ; i <= k ; i++ )
        jc[i] = jc[i-1]*i%mod ; 
    
    if ( x1 != x2 )
        dx[0][0] = 1 ;
    else
        dx[0][1] = 1 ; 
    if ( y1 != y2 )
        dy[0][0] = 1 ;
    else
        dy[0][1] = 1 ; 
    for (int i = 1 ; i <= k ; i++ )
    {
        dx[i][1] = dx[i-1][0]%mod ; 
        dx[i][0] = dx[i-1][1]*(h-1)%mod + dx[i-1][0]*(h-2)%mod ; 
        dy[i][1] = dy[i-1][0]%mod ; 
        dy[i][0] = dy[i-1][1]*(w-1)%mod + dy[i-1][0]*(w-2)%mod ; 
    }
    long long ans = 0 ; 
    for (int i = 0 ; i <= k ; i++ )
    {
        long long sk = dx[i][1]*dy[k-i][1]%mod ; 
        sk = sk*zuhe(k,i)%mod ; 
        ans = (ans + sk)%mod ; 
    }
    cout << ans << "\n" ; 
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值