题目链接:点击查看
题目大意:给出一个 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) 来说:
综上所述,将所有坏点排序之后的转移方程就是(设 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}