魔改森林 题解(dp+容斥+组合数学)

魔改森林

https://ac.nowcoder.com/acm/problem/200324

题目链接

题目大意

给你一个n*m的方格图,中间有k个障碍,要你求从左下角到右上角有多少种方案mod 998244353

img

题目思路

这个题目真是很oi......
首先 直接

如果方格数量很多,观察障碍物很少,则可以想到容斥的思维

然后再用组合数学预处理一下

一定要注意要预处理到2e5 wa了我一辈子

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=2e5+5,inf=0x3f3f3f3f,mod=998244353;
const int eps=1e-6;
int n,m,k;
ll fac[maxn];
ll dp[1000+5][1000+5];
bool mp[1000+5][1000+5];
pair<int,int> pa[maxn];
ll qpow(ll a,ll b){
    ll ans=1,base=a;
    while(b){
        if(b&1){
            ans=ans*base%mod;
        }
        b=b>>1;
        base=base*base%mod;
    }
    return ans;
}
ll C(int a,int b){
    return 1ll*fac[a]*qpow(fac[b],mod-2)%mod*qpow(fac[a-b],mod-2)%mod;
}
void init(){
    fac[0]=1;
    for(int i=1;i<=2e5;i++){
        fac[i]=fac[i-1]*i%mod;
    }
}
bool cmp(pair<int,int> a,pair<int,int> b){
    if(a.fi==b.fi){
        return a.se<b.se;
    }else{
        return a.fi>b.fi;
    }
}
void solve1(){
    for(int i=n+1;i>=1;i--){
        for(int j=1;j<=m+1;j++){
            if(i==n+1&&j==1) dp[i][j]=1;
            else if(mp[i][j]) dp[i][j]=0;
            else    dp[i][j]=(dp[i+1][j]+dp[i][j-1])%mod;
        }
    }
    printf("%lld\n",dp[1][m+1]);
}
void solve2(){
    //(n,0) (0,m)
    sort(pa+1,pa+1+k,cmp);// 排序
    ll ans=C(n+m,n); //总共
    for(int i=1;i<=k;i++){// 走过一个点
        ans-=1ll*C(n-pa[i].fi+pa[i].se,pa[i].se)*C(pa[i].fi+m-pa[i].se,pa[i].fi)%mod;
    }
    bool flag=1;
    for(int i=1;i<k;i++){// 走过两个点
        for(int j=i+1;j<=k;j++){
            if(pa[i].se>pa[j].se){
                flag=0;
                continue;
            }
            ans+=1ll*C(n-pa[i].fi+pa[i].se,pa[i].se)*C(pa[i].fi-pa[j].fi+pa[j].se-pa[i].se,pa[i].fi-pa[j].fi)%mod*C(pa[j].fi+m-pa[j].se,pa[j].fi)%mod;
        }
    }
    if(flag&&k==3){ //走过三个点
        ans-=1ll*C(n-pa[1].fi+pa[1].se,pa[1].se)*C(pa[2].fi-pa[1].fi+pa[1].se-pa[2].se,pa[2].fi-pa[1].fi)%mod*C(pa[3].fi-pa[2].fi+pa[2].se-pa[3].se,pa[3].fi-pa[2].fi)%mod*C(pa[3].fi+m-pa[3].se,pa[3].fi)%mod;
    }
    ans=(ans%mod+mod)%mod;
    printf("%lld\n",ans);
}
int main(){
    init();
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++){
        scanf("%d%d",&pa[i].fi,&pa[i].se);
        pa[i].fi--,pa[i].se--;
        // 格子与格点的区别
        if(k>=10)mp[pa[i].fi+1][pa[i].se+1]=1;
    }
    if(k>=10){
        solve1();
    }else{
        solve2();
    }
    return 0;
}
全部评论

相关推荐

11-05 07:29
贵州大学 Java
点赞 评论 收藏
分享
已老实求offer😫:有点像徐坤(没有冒犯的意思哈)
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务