【题解/总结】两双手(格路问题)/格路问题的某一本质

【题解】两双手(格路问题)

题目大意:求从\((0,0)\)\((Ex,Ey)\)不经过给定障碍点的方案数。你每次移动只能是加上向量\(e_1\)或者向量\(e_2\)\(e_1,e_2\)中的基底都是整数。

考虑转化一下这个问题,从某个点走到在他右上角的某点需要加上\(ae_1+be_2\),这样我们就可以解出\(a,b\)。我们把\((a,b)\)拿出来建立新的坐标系,就变成了简单的格路问题了。结合【题解】CF559C C. Gerald and Giant Chess(容斥+格路问题)就可以直接做了。当不存在\((a,b)\)是非负整数解时,可以把这个点删掉。

考虑为什么可以这样解出两个值\((a,b)\)转化,只能在这道题里用吗?实际上,格路问题可以抽象成这样一种问题:

你有两种元素,每种元素每次可以增加一一次只能加一种元素,元素之间互相独立。现在要你从\((0,0)\)加到\((n,m)\),问你多少组方案。方案的本质是对于加元素的操作构成的合法序列的总数。

用序列的角度看问题,​有\(a_i\)\(i\)元素,你要好好排列出\(\sum a_i\)长度的序列,问这些序列不同的分布。这就可以拓展为多维的问题。

这应该是此类格路问题的本质。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
int n,A1,A2,B1,B2;
const int maxn=5e2+5;
const int mod=1e9+7;
typedef pair<int,int> P;
P data[maxn];
int jc[1000001],inv[1000001],cnt,dp[maxn];


inline int ksm(const int&ba,const int&p){
      int ret=1;
      for(int t=p,b=ba%mod;t;t>>=1,b=1ll*b*b%mod)
        if(t&1) ret=1ll*ret*b%mod;
      return ret;
}

inline void pre(const int&n){
      *jc=*inv=1;
      for(int t=1;t<=n;++t) jc[t]=1ll*jc[t-1]*t%mod;
      inv[n]=ksm(jc[n],mod-2);
      for(int t=n-1;t;--t)  inv[t]=1ll*inv[t+1]*(t+1)%mod;
}

inline bool get(P&s){
      int x=s.first,y=s.second;
      int a=(B2*x-B1*y)/(A1*B2-A2*B1);
      int b=(A2*x-A1*y)/(A2*B1-A1*B2);
      s=(pair<int,int>){a,b};
      return a*A1+b*B1==x&&a*A2+b*B2==y&&a>=0&&b>=0;
}

inline int c(const int&n,const int&m){return n<m?0:1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
inline int grid(const int&x,const int&y){return c(x+y,x);}
int main(){
      int f=qr(),g=qr();
      data[++cnt]=(pair<int,int>){f,g}; n=qr();
      A1=qr(); A2=qr();
      B1=qr(); B2=qr();
      if(!get(data[1])) return !puts("0");
      pre(1e6);
      for(int t=1,t1,t2;t<=n;++t){
        t1=qr(); t2=qr();
        data[++cnt]=(pair<int,int>){t1,t2};
        if(!(get(data[cnt])&&data[cnt]<=data[1])) --cnt;
      }
      sort(data+1,data+cnt+1);
      for(int t=1;t<=cnt;++t){
        dp[t]=grid(data[t].first,data[t].second);
        for(int i=1;i<t;++i)
          dp[t]=(dp[t]-1ll*dp[i]*grid(data[t].first-data[i].first,data[t].second-data[i].second)%mod+mod)%mod;
      }
      printf("%d\n",dp[cnt]);
      return 0;
}

posted @ 2019-09-22 11:48 谁是鸽王 阅读(...) 评论(...) 编辑 收藏