• BZOJ4767 两双手


    题目链接:戳我

    有障碍点的网格图计数

    对于一个位置我们一定会通过特定数量的操作来达到这个位置,而且如果有解的话,这个数量一定是确定的。

    为什么呢?

    我们设(px)为当前点的x坐标,(py)为当前点的y坐标,(ax,ay,bx,by)分别对应题目中含义。那么一定有

    [left{egin{array};a*ax+b*bx=px\a*ay+b*by=pyend{array} ight. ]

    通过消元我们可以得到——

    [left{egin{array};a=frac{px*by-py*bx}{ax*by-ay*bx}\b=frac{px*ay-py*ax}{bx*ay-by*ax}end{array} ight. ]

    然后注意一下分母为0,或者是a,b任意一个不是整数的时候是没有符合题意的解的。

    然后我们只记录所有的关键点(也就是障碍点+终点),我们把它们通过排序对应到一个序列上。把它们的xy坐标用两种移动方式分别要移动多少次来替代。注意要排除掉不合法的点。

    (dp[i])表示从原点到第i个点时,不经过任何障碍点的路径条数。

    我们知道对于一个确定的点((x,y)),走到它的路径条数为(C_{x+y}^x)。(为什么明明在组合数上是等价的,在这里却不写(C_{x+y}^{y})呢?这个我们在下面再提)

    我们有转移方程为(dp[i]=C_{x[i]+y[i]}^{x[i]}-sum_{j=1}^{i-1}dp[j] imes C_{x[i]-x[j]+y[i]-y[j]}^{x[i]-y[i]})

    为什么这样子是对的?

    自行思考一下嘛这个容斥总觉得有点类似求一堆数中两两配对个数,(x,y)等同于(y,x)那种qwqwq唔感觉说不清楚了。。。。

    为什么排序优先级x大于y呢?(不考虑这个问题竟然在darkbzoj上还能过!数据太弱了吧qwqwq)因为我们在下面使用的是(C_{x+y}^x),而当(x>x+y)的时候,return 0。按照给定的两种方式来作为平面上的基底,假设现在它们指向右和上,那么显然左和下是不能过去的。(额,表达能力有点弱,大家理解一下??)如果使用(C_{x+y}^y)的话,我们就要y排序优先级大于x了。

    为什么最后输出dp[n]就可以了?因为我们放坐标前就已经按照我们转化的方法把不合法的都扔掉了,终点在排序之后也一定是n(最后面的这个)。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define MAXN 2500010
    #define mod 1000000007
    using namespace std;
    int n,ax,ay,bx,by;
    int nn[MAXN+2],kk[MAXN+2],dp[MAXN];
    struct Node{int x,y;}t[MAXN];
    bool cmp(Node a,Node b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
    inline int pow(int x,int y)
    {
        int cur_ans=1;
        while(y)
        {
            if(y&1) cur_ans=1ll*cur_ans*x%mod;
            x=1ll*x*x%mod;
            y>>=1;
        }
        return cur_ans%mod;
    }
    inline void init()
    {
        kk[0]=nn[0]=1;
        for(int i=1;i<MAXN;i++) kk[i]=1ll*kk[i-1]*i%mod;
        nn[MAXN-1]=pow(kk[MAXN-1],mod-2)%mod;
        for(int i=MAXN-2;i>=0;i--) nn[i]=1ll*nn[i+1]*(i+1)%mod; 
    }
    inline void solve(int &px,int &py)
    {
        int cur1=px*by-py*bx;
        int cur2=ax*by-ay*bx;
        int cur3=px*ay-py*ax;
        int cur4=bx*ay-by*ax;
        if(!cur2||!cur4) {px=-1,py=-1;return;} 
        if((cur1%cur2)||(cur3%cur4)) {px=-1,py=-1;return;}
        px=cur1/cur2,py=cur3/cur4;
    }
    inline int calc(int x,int y)
    {
        if(x<y) return 0;
        else return 1ll*kk[x]*nn[y]%mod*nn[x-y]%mod;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        //freopen("ce.out","w",stdout);
        #endif
        scanf("%d%d%d%d%d%d%d",&t[0].x,&t[0].y,&n,&ax,&ay,&bx,&by);
        solve(t[0].x,t[0].y);
        for(int i=1;i<=n;i++) 
        {
            scanf("%d%d",&t[i].x,&t[i].y);
            solve(t[i].x,t[i].y);
            if(t[i].x<0||t[i].y<0||t[i].x>t[0].x||t[i].y>t[0].y) i--,n--;
        }
        n++;t[n].x=t[0].x,t[n].y=t[0].y;
        sort(&t[1],&t[n+1],cmp);
        init();
        for(int i=1;i<=n;i++)
        {
            dp[i]=calc(t[i].x+t[i].y,t[i].x);
            for(int j=1;j<i;j++)
            {
                int xx=t[i].x-t[j].x;
                int yy=t[i].y-t[j].y;
                dp[i]=(dp[i]-1ll*dp[j]*calc(xx+yy,xx)%mod+mod)%mod;
            }
        }
        printf("%d
    ",dp[n]%mod);
        return 0;
    }
    

    qwq,今天颓废了一天。。。才写了这一道题qwqwq

  • 相关阅读:
    面向对象之多态,property
    描述符
    day23 面向对象之继承
    day22面向对象
    os模块
    logging日志模块,四种方式
    Linux 如何测试 IO 性能(磁盘读写速度)
    Vi命令:如何删除全部内容
    cdnbest如何查看站点操作日志(同步日志)
    Linux查找含有某字符串的所有文件
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10422970.html
Copyright © 2020-2023  润新知