n*n
的网格,有些格子可以走,有些不可以走,不能走的格子的坐标已经给了出来,问你从(1,1)
开始,只能往右或者往下走,到(n,n)
有多少种路径的方案
挺有意思的一道题,跟前些日子纳新笔试题的倒数第二题有点点类似,但又不同
首先一个需要知道的东西是,从
(1,1)
走到(n,m)
,每次只能往右或者往下走的方案数应该是C(n-1+m-1, n-1)
首先一看数据范围
n=1e6
,m=3000
,就能想到应该是个m*m
的一个dp 差不多我们可以考虑
dp[i]
表示从(1,1)
出发到(xi, yi)
的障碍物,过程不经过其他障碍物的方案数,则我们可以利用“减法”进行状态的计算,即用C(xi-1+yi-1, xi-1)
的总的方案数减去其他障碍物j
产生的影响。显然xj<=xi && yj<=yi
才会对i
产生影响,我们就可以从那些状态转移过来,而障碍物(xj,yj)
对(xi,yi)
产生的影响应该是dp[j]*C(xi-xj+yi-yj, xi-xj)
由于循环不是很好写,需要排序来确定循环顺序,不如直接记忆化,用到什么搜什么
(组合数的板子抄的ygg的捏
#include
using namespace std;
#define int long long
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 2000000 + 50
int n, m, k, x;
struct ran{
int x, y;
}tr[MAX];
namespace CNM {
const int N = 2e6 + 5;
ll mod = 1e9+7;
ll quick(ll x, ll n)
{
ll res = 1;
while (n)
{
if (n & 1) res = (res*x) % mod;
x = x * x%mod;
n >>= 1;
}
return res;
}
ll inv(ll x) { return quick(x, mod - 2); }
ll fac[N], invfac[N];
void init()
{
fac[0] = 1;
for (int i = 1; i < N; ++i) fac[i] = (fac[i - 1] * i) % mod;
invfac[N - 1] = inv(fac[N - 1]);
for (int i = N - 2; i >= 0; --i) invfac[i] = (invfac[i + 1] * (i + 1)) % mod;
}
ll C(int n, int m)
{
if (n < m || m < 0) return 0;
return fac[n] * invfac[m] % mod*invfac[n - m] % mod;
}
}
ll dp[MAX];
bool vis[MAX];
ll dfs(int i){
if(vis[i])return dp[i];
vis[i] = 1;
ll ans = 0;
for(int j = 1; j <= m; ++j){
if(i == j)continue;
if(tr[j].x <= tr[i].x && tr[j].y <= tr[i].y){
(ans += (dfs(j) * CNM::C(tr[i].x-tr[j].x+tr[i].y-tr[j].y, tr[i].x-tr[j].x)%mod7)%mod7)%=mod7;
}
}
dp[i] = ((CNM::C(tr[i].x+tr[i].y-2, tr[i].x-1) - ans)%mod7 + mod7)%mod7;
return dp[i];
}
void work(){
CNM::init();
cin >> n >> m;
for(int i = 1; i <= m; ++i){
cin >> tr[i].x >> tr[i].y;
}
tr[++m].x = n;tr[m].y = n;
dfs(m);
cout << dp[m] << endl;
}
signed main(){
io;
work();
return 0;
}