BZOJ4767 两双手

题目链接:戳我

有障碍点的网格图计数

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

为什么呢?

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

\[\left\{\begin{array}\;a*ax+b*bx=px\\a*ay+b*by=py\end{array}\right. \]

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

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

然后注意一下分母为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]\times 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\n",dp[n]%mod);
    return 0;
}

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

posted @ 2019-02-23 15:56  风浔凌  阅读(163)  评论(0编辑  收藏  举报