bzoj4767 两双手(dp+容斥+组合数学)

在二维平面上,你一开始在(0,0),给定两个向量,问你有多少种不经过坏点的方式到达(x,y)。
对于每一个点都求出需要走几个向量1,几个向量2,显然是唯一的。如果不合法直接跳过。
然后就转化成了类似上一题的模型。dp+容斥+组合数学解决。

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 510
#define mod 1000000007
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int inv[500010],fac[500010],n,m,a1,b1,a2,b2,f[N];
struct point{
    int x,y;
    friend bool operator<(point a,point b){return a.x==b.x?a.yinline void solve(int x,int y){
    int t1=x*b1-y*a1,t2=a2*b1-b2*a1;
    if(t1%t2) return;int aa=t1/t2;
    t1=x*b2-y*a2,t2=a1*b2-b1*a2;
    if(t1%t2) return;int bb=t1/t2;
    a[++n].x=aa;a[n].y=bb;
}
inline int C(int x,int y){return (ll)fac[x]*inv[y]%mod*inv[x-y]%mod;}
inline void inc(int &x,int y){x+=y;x%=mod;}
int main(){
//  freopen("a.in","r",stdin);
    int tx=read(),ty=read();m=read();inv[0]=1;inv[1]=1;fac[0]=1;
    a1=read();b1=read();a2=read();b2=read();
    solve(tx,ty);if(!n){puts("0");return 0;}
    for(int i=2;i<=500000;++i) inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
    for(int i=1;i<=500000;++i) fac[i]=(ll)fac[i-1]*i%mod,inv[i]=(ll)inv[i-1]*inv[i]%mod;
    while(m--){
        int x=read(),y=read();solve(x,y);
        if(a[n].x>a[1].x||a[n].y>a[1].y||a[n].x<0||a[n].y<0) --n;
    }sort(a+1,a+n+1);
    for(int i=1;i<=n;++i){
        f[i]=C(a[i].x+a[i].y,a[i].x);
        for(int j=1;jif(a[j].y>a[i].y) continue;
            inc(f[i],(ll)f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mod*(mod-1)%mod);
        }
    }printf("%d\n",f[n]);
    return 0;
}

你可能感兴趣的:(bzoj,容斥原理,组合数学)