BZOJ4767-两双手-DP+容斥

传送门

题意:

棋盘上的一个棋子,给出他的两种移动方式:

1. (u,v)>(u+Ax,v+Ay) ( u , v ) − > ( u + A x , v + A y )

2. (u,v)>(u+Bx,v+By) ( u , v ) − > ( u + B x , v + B y )

现给出一些不能走的障碍点n个,求(0,0)到(Ex,Ey)的方案数

|Ax|,|Ay|,|Bx|,|By|<=500,0<=n,Ex,Ey<=500;AxByAyBx0 | A x | , | A y | , | B x | , | B y | <= 500 , 0 <= n , E x , E y <= 500 ; A x ∗ B y − A y ∗ B x ≠ 0

Solution:

因为题目保证了 AxByAyBx0 A x ∗ B y − A y ∗ B x ≠ 0 ,所以从原点出发走到某个点所需的两种走法的次数是唯一的

具体可以列出方程,设到点(X,Y)分别需要a,b步

{Axa+Bxb=XAya+Byb=Y { A x ∗ a + B x ∗ b = X A y ∗ a + B y ∗ b = Y

我们只要把指定点的a,b算出来后,问题就转化为一个只能往右或者往上的常规路径计数问题

但是(Ex,Ey)所对应的a,b可能很大,所以我们不能递推地算

考虑容斥:

我们知道如果没有障碍的话从 (0,0) ( 0 , 0 ) (n,m) ( n , m ) 的方案数为 Cnn+m C n + m n

f[i] f [ i ] 为从原点出发到达第i个关键点的方案数, g[i][j] g [ i ] [ j ] 表示从i到j的方案数

那么我们可以得到: f[i]=g[0][i]i1j=1f[j]g[j][i] f [ i ] = g [ 0 ] [ i ] − ∑ j = 1 i − 1 f [ j ] ∗ g [ j ] [ i ]

显然g数组就是一个组合数,预处理阶乘以及阶乘的逆元即可

复杂度 O(n2) O ( n 2 )

代码:

#include
#include
#include
using namespace std;
const int mod=1e9+7;
const int N=1000000;
int n,m,t,cnt;
int f[510];
int a,b,c,d,mi[1000010],inv[1000010];
struct P{
    int x,y;
}p[510];
bool calc(int &x,int &y)
{
    int s1=x*d-y*c,t1=a*d-b*c,s2=x*b-y*a,t2=c*b-d*a;
    if (t1==0||t2==0) return 0;
    if (s1%t1) return 0;else x=s1/t1;
    if (s2%t2) return 0;else y=s2/t2;
    return 1; 
}
int fast_pow(int x,int a)
{
    int ans=1;
    for (;a;a>>=1,x=1ll*x*x%mod)
    {
        if (a&1) ans=1ll*ans*x%mod;
    }
    return ans;
}
bool cmp(P a,P b){if(a.x==b.x) return a.yy; return a.xx;}
int C(int n,int m){if (n<m) return 0;return 1ll*mi[n]*inv[m]%mod*inv[n-m]%mod;}
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    scanf("%d%d%d%d",&a,&b,&c,&d);
    if ((!calc(n,m))||n<0||m<0) {printf("0\n");return 0;}
    for (int x,y,i=1;i<=t;i++)
    {
        scanf("%d%d",&x,&y);
        if (calc(x,y)&&x>=0&&y>=0&&x<=n&&y<=m) p[++cnt]={x,y};
    }
    mi[0]=1;
    for (int i=1;i<=N;i++) mi[i]=1ll*mi[i-1]*i%mod;
    inv[N]=fast_pow(mi[N],mod-2);
    for (int i=N-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
    p[++cnt]={n,m};
    sort(p+1,p+1+cnt,cmp);
    f[0]=1;
    for (int i=1;i<=cnt;i++)
    {
        f[i]=C(p[i].x+p[i].y,p[i].x);
        for (int j=1;j1ll*f[i]+mod-(1ll*C(p[i].x-p[j].x+p[i].y-p[j].y,p[i].x-p[j].x)*f[j]%mod))%mod;
    }
    printf("%d",f[cnt]);
}

你可能感兴趣的:(容斥,DP)