bzoj 4767: 两双手

题意:

给出两个向量,问多少种方式能够不经障碍点到达终点。

题解:

因为向量可以唯一拆分成两个向量,所以就成了网格图计数。
为了方便,将终点也视为障碍点,设 f [ i ] f[i] f[i]表示到第i个障碍点的方案数。
若不考虑其它障碍点,那么就是 C x + y y C_{x+y}^y Cx+yy,然后减掉从其它障碍点到这个点的情况就好了。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL X,Y,n,x,y,x1,y1;
LL f[510];
bool vis[510];
LL fac[1000010],inv[1000010];
struct node{
    LL x,y;
}a[510];
void pre()
{
    inv[0]=inv[1]=fac[0]=fac[1]=1;
    for(int i=2;i<=1000000;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    for(int i=2;i<=1000000;i++) inv[i]=inv[i-1]*inv[i]%mod,fac[i]=fac[i-1]*i%mod;
}
LL C(LL m,LL n) {return fac[m]*inv[m-n]%mod*inv[n]%mod;}
node get(LL s1,LL s2)
{
    node ans;ans.x=-1;
    LL A=x1*y-x*y1,B=s1*y-x*s2;
    if(B%A!=0) return ans;
    ans.y=B/A;
    A=x;B=s1-ans.y*x1;
    //printf("A B:%lld %lld %lld\n",A,B,B%A);
    if(A==0) A=y,B=s2-ans.y*y1;
    if(B%A!=0) return ans;
    ans.x=B/A;
    //printf("ans:%lld %lld\n",ans.x,ans.y);
    return ans;
}
LL get(LL k)
{
    //printf("%lld %lld %lld\n",k,a[k].x,a[k].y);
    if(vis[k]) return f[k];
    vis[k]=true;f[k]=C(a[k].x+a[k].y,a[k].y);
    for(LL i=1;i<=n;i++)
    {
        if(i==k) continue;
        if(a[i].x<=a[k].x&&a[i].y<=a[k].y&&a[i].x!=-1)
        {
            //printf("%lld->%lld\n",k,i);
            LL t1=a[k].x-a[i].x,t2=a[k].y-a[i].y;
            (f[k]-=C(t1+t2,t1)*get(i)%mod)%=mod;
        }
    }
    //printf("s:%lld %lld %lld %lld\n",k,f[k],a[k].x+a[k].y,a[k].y);
    return f[k];
}
int main()
{
    pre();
    scanf("%lld %lld %lld",&X,&Y,&n);
    scanf("%lld %lld %lld %lld",&x,&y,&x1,&y1);
    for(LL i=1;i<=n;i++)
    {
        LL s1,s2;scanf("%lld %lld",&s1,&s2);
        a[i]=get(s1,s2);
    }
    a[++n]=get(X,Y);
    //for(LL i=1;i<=n;i++) printf("%lld %lld\n",a[i].x,a[i].y);
    //printf("ok\n");
    if(a[n].x==-1) return puts("0"),0;
    memset(vis,false,sizeof(vis));
    for(LL i=1;i<=n;i++)
    {
        //printf("%lld\n",i);
        if(a[i].x==-1) continue;
        if(vis[i]) continue;
        f[i]=get(i);
    }
    printf("%lld\n",(f[n]+mod)%mod);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值