题目链接:点击查看
题目大意:给出一个 n ? m n*m n?m 的矩阵,其中有 k k k 个坏点,每次只能向右走或向下走,问从点 ( 1 , 1 ) (1,1) (1,1) 到点 ( n , m ) (n,m) (n,m) 共有多少种不同的路线
题目分析:刷知乎看到的一道题,心血来潮就想写题了
数据范围 n , m n,m n,m 都是 1 e 5 1e5 1e5 级别的,而 k k k 却只有 2000 2000 2000,所以从坏点入手,考虑 d p dp dp 和组合数学
首先对于两个点 ( x 1 , y 1 ) (x1,y1) (x1,y1) 和 ( x 2 , y 2 ) (x2,y2) (x2,y2) 来说,如果没有坏点的限制,那么这两个点之间的路线有 C ( x 2 ? x 1 ) + ( y 2 ? y 1 ) x 2 ? x 1 C_{(x2-x1)+(y2-y1)}^{x2-x1} C(x2?x1)+(y2?y1)x2?x1? 条,下面可以分两步进行思考,对于每个坏点 ( x , y ) (x,y) (x,y) 来说:
- 加上点 ( 1 , 1 ) (1,1) (1,1) 到点 ( x , y ) (x,y) (x,y) 之间方案数
- 对于每个满足 x x < = x xx<=x xx<=x 且 y y < = y yy<=y yy<=y 的坏点 ( x x , y y ) (xx,yy) (xx,yy) 来说,点 ( 1 , 1 ) (1,1) (1,1) 经过点 ( x x , y y ) (xx,yy) (xx,yy) 然后到达点 ( x , y ) (x,y) (x,y) 的这些路线显然是不合法的,减去即可
综上所述,将所有坏点排序之后的转移方程就是(设
f
(
x
1
,
y
1
)
(
x
2
,
y
2
)
f_{(x1,y1)}^{(x2,y2)}
f(x1,y1)(x2,y2)? 为坏点
(
x
1
,
y
1
)
(x1,y1)
(x1,y1) 到坏点
(
x
2
,
y
2
)
(x2,y2)
(x2,y2) 的方案数,
d
p
i
dp_i
dpi? 为点
(
1
,
1
)
(1,1)
(1,1) 到第
i
i
i 个坏点的方案数):
d
p
i
=
f
(
1
,
1
)
(
p
i
.
x
,
p
i
.
y
)
?
[
p
j
.
x
<
=
p
i
.
x
?
&
&
?
p
j
.
y
<
=
p
i
.
y
]
?
d
p
j
?
f
(
p
i
.
x
,
p
i
.
y
)
(
p
j
.
x
,
p
j
.
y
)
dp_i=f_{(1,1)}^{(p_i.x,p_i.y)} \\ -[p_j.x<=p_i.x\ \&\&\ p_j.y<=p_i.y]*dp_j*f_{(p_i.x,p_i.y)}^{(p_j.x,p_j.y)}
dpi?=f(1,1)(pi?.x,pi?.y)??[pj?.x<=pi?.x?&&?pj?.y<=pi?.y]?dpj??f(pi?.x,pi?.y)(pj?.x,pj?.y)?
将点
(
n
,
m
)
(n,m)
(n,m) 设为第
k
+
1
k+1
k+1 个坏点,那么答案自然就是
d
p
k
+
1
dp_{k+1}
dpk+1? 了
代码:
// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{
T f=1;x=0;
char ch=getchar();
while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=f;
}
template<typename T>
inline void write(T x)
{
if(x<0){x=~(x-1);putchar('-');}
if(x>9)write(x/10);
putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e6+100;
const int mod=1e9+7;
struct Point {
int x,y;
void input() {
read(x),read(y);
}
bool operator<(const Point& t)const {
if(x!=t.x) {
return x<t.x;
}
return y<t.y;
}
}p[N];
LL dp[N],fac[N],inv[N];
LL q_pow(LL a,LL b) {
LL ans=1;
while(b) {
if(b&1) {
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans;
}
LL C(int n,int m) {
if(n<m) {
return 0;
}
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void init() {
fac[0]=1;
for(int i=1;i<N;i++) {
fac[i]=fac[i-1]*i%mod;
}
inv[N-1]=q_pow(fac[N-1],mod-2);
for(int i=N-2;i>=0;i--) {
inv[i]=inv[i+1]*(i+1)%mod;
}
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
int n,m,k;
read(n),read(m),read(k);
for(int i=1;i<=k;i++) {
p[i].input();
}
p[++k]={n,m};
sort(p+1,p+1+k);
for(int i=1;i<=k;i++) {
dp[i]=C((p[i].x-1)+(p[i].y-1),(p[i].x-1));
for(int j=1;j<i;j++) {
if(p[i].x>=p[j].x&&p[i].y>=p[j].y) {
dp[i]=(dp[i]-dp[j]*C((p[i].x-p[j].x)+(p[i].y-p[j].y),p[i].x-p[j].x)%mod+mod)%mod;
}
}
}
write(dp[k]);
return 0;
}