1. 程式人生 > >BZOJ4767 兩雙手

BZOJ4767 兩雙手

tdi esp 順序 sum www. for lse tor getchar

BZOJ4767 兩雙手

題面:BZOJ

解析

容斥水題。先解出走到結束點和禁止點的步數(不難發現這個步數是唯一的)。然後去掉絕對不會走到的禁止點,現在考慮容斥去除禁止點的影響,不難想到+-1的容斥方法,但是怎麽確定貢獻呢?對於至少走\(i\)個禁止點,方案數就是強制走\(i\)個點的方案數,其後再隨意分配即可。這樣做很難做,不妨變換枚舉順序,改為枚舉每一個點作為最後一個被走到的禁止點,這樣可以\(O(n^2)\)的預處理方案數,在中途+-1即可。我也不知道我是怎麽亂搞過的

代碼


#include<cstdio>
#include<algorithm>
#define N 505
using namespace std;
const int P=1e9+7,__=1e6;
inline int In(){
    char c=getchar(); int x=0,ft=1;
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    return x*ft;
}
inline int max(int a,int b){
    return a>b?a:b;
}
int Ex,Ey,Ax,Ay,Bx,By,A,B,t[N],n,lim,qc=0,ans=0,g[N],fac[__],inv[__];
struct Q{
    int a,b;
    bool operator < (const Q& t) const { return a==t.a?b<t.b:a<t.a; }
}q[N];
inline int power(int x,int k){
    int s=1,t=x;
    for(;k;k>>=1,t=1ll*t*t%P) if(k&1) s=1ll*s*t%P;
    return s;
}
inline void Sol(int x,int y,int& t1,int& t2){
    if((Bx*y-By*x)%(Ay*Bx-Ax*By)==0&&(Ax*y-Ay*x)%(By*Ax-Bx*Ay)==0)
    t1=(Bx*y-By*x)/(Ay*Bx-Ax*By),t2=(Ax*y-Ay*x)/(By*Ax-Bx*Ay);
    else t1=2e9,t2=2e9;
}
inline int f(int i,int j){
    return 1ll*fac[i+j]*inv[i]%P*inv[j]%P;
}
int main(){
    Ex=In(); Ey=In(); n=In(); Ax=In(); Ay=In(); Bx=In(); By=In();
    Sol(Ex,Ey,A,B); if(A==2e9){ printf("%d\n",0); return 0;}
    lim=A+B; fac[0]=1; for(int i=1;i<=lim;++i) fac[i]=1ll*fac[i-1]*i%P;
    inv[lim]=power(fac[lim],P-2); for(int i=lim-1;~i;--i) inv[i]=1ll*inv[i+1]*(i+1)%P;
    for(int i=1,a,b,Sx,Sy;i<=n;++i){
        Sx=In(); Sy=In(); Sol(Sx,Sy,a,b);
        if(a<=A&&b<=B) q[++qc].a=a,q[qc].b=b;
    }
    n=qc; sort(q+1,q+1+n);
    for(int i=1;i<=n;++i){
        g[i]=f(q[i].a,q[i].b);
        for(int j=1;j<i;++j) if(q[j].a<=q[i].a&&q[j].b<=q[i].b)
        g[i]=(g[i]-1ll*g[j]*f(q[i].a-q[j].a,q[i].b-q[j].b)%P+P)%P;
    }
    ans=f(A,B);
    for(int i=1,sum;i<=n;++i)
    ans=(ans+1ll*(P-1)*f(A-q[i].a,B-q[i].b)%P*g[i]%P)%P;
    printf("%d\n",ans);
    return 0;
}

BZOJ4767 兩雙手