链接:https://ac.nowcoder.com/acm/contest/4474/A
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
曾经有一道叫做迷雾森林的题目,然而牛牛认为地图中的障碍太多,实在是太难了,所以删去了很多点,出了这道题。
牛牛给出了一个n行m列的网格图
初始牛牛处在最左下角的格点上(n+1,1),终点在右上角的格点(1,m+1)
现在它想知道,从起点走到终点,只能向上或向右走,一共有多少种走法呢?
需要注意的是,除了起点和终点外,其它的每个格点都有可能有障碍,无法通过。
请注意格子与格点的区别
输入描述:
第一行三个整数n,m,k,表示格子的大小,以及有k个障碍 接下来k行(若k=0则无此行),每行一个格点坐标(x,y),表示每个障碍的位置
输出描述:
仅一行,一个整数表示答案对998244353取模的值
示例1
输入
复制4 5 1 3 4
4 5 1 3 4
输出
复制66
66
说明
示例2
输入
复制12345 54321 2 123 321 456 654
12345 54321 2 123 321 456 654
输出
复制801071140
801071140
示例3
输入
复制114 514 3 19 19 8 10 65 45
114 514 3 19 19 8 10 65 45
输出
复制567102428
567102428
备注:
比较无聊就去牛客打了场oi,然后发现不太会
只写了个暴力的想法,赛后倒是看到了我这个暴力想法的后续,不得不说,大佬nb
但其实这个题可以容斥orz
有一个差不多和偏序关系相似的地方
假设只有一个点不能走的话,那么用总的(C(n+m-2,n-1))。减掉经过这个点(C(x+y-2,x-1)*C(n-x+n-y,n-x))的即可
即C(n+m-2,n-1)-C(x+y-2,x-1)*C(n-x+n-y,n-x)
如果有多个个点的话容斥。
根据位置,如果存在一个偏序关系的话,那么是可以叠加的。
就是说起点->A->B->.....->终点,这个样子,如图
如果不存在偏序关系,那么这次的选择自然是不被计算的。
#include <bits/stdc++.h>
/*author:revolIA*/
/*明日を変えるなら今日変えなきゃ*/
using namespace std;
const int mod = 998244353;
const int N = 1e6+7;
int n,m,k;
int mmp[N];
void solve1(){
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
mmp[i*(m+1)+j] = 0;
}
}
mmp[0] = 1;
while(k--){
int x,y;
scanf("%d%d",&x,&y);
--x,--y;
x = n-x;
mmp[x*(m+1)+y] = -1;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++)if(~mmp[i*(m+1)+j]){
if(i && ~mmp[(i-1)*(m+1)+j])mmp[i*(m+1)+j] = (mmp[(i-1)*(m+1)+j]+mmp[i*(m+1)+j])%mod;
if(j && ~mmp[i*(m+1)+j-1])mmp[i*(m+1)+j] = (mmp[i*(m+1)+j-1]+mmp[i*(m+1)+j])%mod;
}
}
printf("%d\n",mmp[(n+1)*(m+1)-1]);
}
int F[N] = {1},inv[N];
int Pow(int a,int b,int ans = 1){
for(a%=mod;b;b>>=1,a=1LL*a*a%mod)if(b&1)ans=1LL*ans*a%mod;
return ans;
}
int C(int n,int m){
if(n<0||m<0||n-m<0)return 0;
return (int)(1LL*F[n]*inv[n-m]%mod*inv[m]%mod);
}
void solve2(){
pair<int,int> p[15];
for(int i=0;i<k;i++){
scanf("%d%d",&p[i].first,&p[i].second);
--p[i].first,--p[i].second;
p[i].first = n-p[i].first;
}
sort(p,p+k);
int ans = C(n+m,n);
for(int s=1;s<(1<<k);s++){
int lasx = 0,lasy = 0,num = 0,Tmp = 1;
for(int i=0;i<k;i++)if((1<<i)&s){
num++;
if(lasx > p[i].first || lasy > p[i].second)Tmp = 0;
else Tmp = 1LL*Tmp*C(p[i].first+p[i].second-lasx-lasy,p[i].first-lasx)%mod;
lasx = p[i].first,lasy = p[i].second;
}
Tmp = 1LL*Tmp*C(n+m-lasx-lasy,n-lasx)%mod;
if(num&1)ans = (1LL*ans-Tmp+mod)%mod;
else ans += Tmp,ans %= mod;
}
printf("%d\n",ans);
}
int main(){
for(int i=1;i<N;i++)F[i] = (1LL*i*F[i-1])%mod;
inv[N-1] = Pow(F[N-1],mod-2);
for(int i=N-2;~i;i--)inv[i] = (1LL*(i+1)*inv[i+1])%mod;
scanf("%d%d%d",&n,&m,&k);
(1LL*n*m < 1000000LL)?solve1():solve2();
return 0;
}