[容斥原理 DP] BZOJ 4767 两双手

版权声明:本文为博主原创文章,未经博主允许随意转载。 https://blog.csdn.net/u014609452/article/details/60778750

首先一个向量可以被两个向量唯一表示 然后就转化为有障碍点的网格图路径计数
这应该是个经典的容斥模型 果然是NOIP模拟赛啊
如果不考虑障碍 那么答案是path(s,t)=Cmn+m
然后我们考虑容斥 枚举路径上碰到的第一个障碍点v

Ans=Cmn+mfvpath(v,t)

其中fv表示从起点s出发不经过其他障碍点到达v的路径数 path(v,t) 表示从vt不考虑障碍的路径数 这显然也是个组合数
fv怎么求 从头开始DP就好了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<set>
#define read(x) scanf("%d",&(x))
using namespace std;
typedef pair<int,int> abcd;
typedef long long ll;

const int P=1e9+7;
const int MAXN=1000005;

ll fac[MAXN],inv[MAXN];

inline void Pre(int n=500000){
  fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;
  inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(ll)(P-P/i)*inv[P%i]%P;
  inv[0]=1; for (int i=1;i<=n;i++) inv[i]=inv[i]*inv[i-1]%P;
}
inline ll C(int n,int m){
  return fac[n]*inv[m]%P*inv[n-m]%P;
}

int ex,ey,ax,ay,bx,by;

inline bool calc(int x,int y,int &A,int &B){
  int s=x*by-y*bx,t=ax*by-ay*bx;
  if (s%t) return 0; else A=s/t;
  s=x*ay-y*ax,t=bx*ay-by*ax;
  if (s%t) return 0; else B=s/t;
  return 1;
}
const int N=505;
#define X first
#define Y second
int n,m;
int tot; abcd pt[N];
int f[N];

inline ll path(int j,int i){
  int dx=pt[i].X-pt[j].X;
  int dy=pt[i].Y-pt[j].Y;
  if (dx<0 || dy<0) return 0;
  return C(dx+dy,dx);
}

int main(){
  int A,B; int x,y; Pre();
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(ex); read(ey); read(tot);
  read(ax); read(ay); read(bx); read(by);
  if (!(calc(ex,ey,A,B) && A>=0 && B>=0))
    return printf("0\n"),0;
  else
    n=A,m=B;
  int tmp=tot; tot=0;
  for (int i=1;i<=tmp;i++){
    read(x); read(y);
    if (calc(x,y,A,B) && A>=0 && B>=0 && A<=n && B<=m)
      pt[++tot]=abcd(A,B);
  }
  pt[++tot]=abcd(n,m);
  sort(pt+1,pt+tot+1);
  f[0]=1;
  for (int i=1;i<=tot;i++){
    f[i]=path(0,i);
    for (int j=1;j<i;j++)
      f[i]=(f[i]+P-(ll)f[j]*path(j,i)%P)%P;
  }
  printf("%d\n",f[tot]);
  return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页