• https://vjudge.net/problem/CodeForces-559C
  • 题意
    • 给一个h*w(<=1e5)的棋盘,左上角是(1,1),右下角是(h,w)
    • 给n(<=2000)个坐标表示不能走的黑点
    • 问从左上角走到右下角有几种方法
  • 题解
    • 显然,这是一个不可能暴力的计数类问题
    • 题中的两维都显得过大,所以我们从比较少的不能走的黑点入手
    • 假设黑点不存在,那么从坐上角到右下角的方法数为
    • 那么黑点存在时,方法数就是 上式-至少经过一个黑点的方法数
    • 那么求解子问题的关键就是:求出从左上角到(xi,yi)的至少经过一个黑点的方法数
    • 在求解计数dp问题时,我们需要找到一个“基准点”,围绕这个基准点构造一个不可以划分的“整体”,以避免子问题之间的重叠
    • 既然要求左上角到(xi,yi)的至少经过一个黑点的方法数,那么我们就枚举第一个经过的格子,使得左上角到第一个黑点成为一个整体,因为对应经过的第一个黑点不同,所以路线肯定不重复
    • 我们将黑点编号,按行列递增编为1~n,将(1,1)与(h,w)分别看作黑点0、n+1
    • 那么我们用f[i]表示从左上角到达第i个黑点且途径点不为黑点的方法数
    • 那么我们用f[i]之前的所有状态对f[i]进行状态转移,那么路线也不会被遗漏
    • 状态转移方程为:
  • 代码
    • #include<bits/stdc++.h>
      using namespace std;
      #define rep(i,a,n) for(int i=a;i<=n;i++)
      #define per(i,a,n) for(int i=n;i>=1;i--)
      typedef long long ll; 
      typedef unsigned long long ull;
      const ll mod=1e9+7;
      const int INF=0x3f3f3f3f;
      int h,w,n;
      const int maxn=200000;
      ll inv[maxn+10],jc[maxn+10], f[maxn];
      pair<int ,int >p[2019];
      ll power(ll a,ll b){
      	ll ans=1;
      	while(b){
      		if(b&1)ans=a*ans%mod;
      		a=a*a%mod;
      		b/=2;
      	}
      	return ans;
      }
      ll C(int n,int m){
      	return jc[n]*inv[m]%mod*inv[n-m]%mod;
      }
      int main(){
      	jc[0]=1,inv[0]=1;
      	rep(i,1,200000){
      		jc[i]=jc[i-1]*i%mod;
      		inv[i]=power(jc[i],mod-2);
      	}
      	cin>>h>>w>>n;
      	rep(i,1,n){
      		scanf("%I64d%I64d",&p[i].first,&p[i].second);
      	}
      	sort(p+1,p+n+1);
      	p[n+1].first=h,p[n+1].second=w;
      	f[0]=0;
      	rep(i,1,n+1){
      		f[i]=C(p[i].first+p[i].second-2,p[i].first-1);
      		rep(j,1,i-1){
      			if(p[j].first<=p[i].first&&p[j].second<=p[i].second){
      				f[i]=(f[i]-f[j]*C(p[i].first-p[j].first+p[i].second-p[j].second,p[i].first-p[j].first))%mod;
      			}
      		}
      	}
      	cout<<(f[n+1]+mod)%mod<<endl;
      	return 0;
      }
      

发表评论

邮箱地址不会被公开。 必填项已用*标注