Description
给出平面上坐标单调不降的三个矩形 \(A,B,C\) ,你需要在 \(A\) 选择一个起点, \(B\) 选择一个位置休息, \(C\) 选择一个终点,期间你可以向上和向右走
求所有选择的方案和
题面
Solution
写起来有点恶心
先考虑一个简单的问题,求 从 \((0,0)\) 出发,走到矩形 \((0,0),(x,y)\) 内任意一点的方案数
用杨辉三角合并得到:
再用杨辉三角合并成:
这个合并的过程可以抽象的理解为杨辉三角的合并,也可以分析组合意义:
\(\sum_{i=0}^{n}C_{i+x}^{x}\)相当于枚举了一个总数,在这个总数中取 \(x\) 个
我们可以转化为 \(C_{n+x+1}^{x+1}\), 相当于多取出了一个球,其中第 \(x+1\) 个球的位置作为这个枚举的总数,也就是上式的 \(i+x\)
然后求从 \((0,0)\) 走到矩形 \((x1,y1),(x2,y2)\) 内的任意一点的方案就可以用二维前缀和求出了
再回到这题:
考虑枚举中转点,我们不如枚举进入矩形 \(B\) 的点和走出的点,那么路上经过的点都可以作为休息点,那么算出每一对进出点的贡献再乘上路径长度就行了
设这两个点分别为 \((x1,y1),(x2,y2)\)
那么分三段算就行了 (F(1)+F(2)+F(3))*(x2-x1+y2-y1+1)
实际上把 (x2+y2+1) 和 (-x1-y1) 的贡献分开算就可以做到 \(O(n)\) 的了,至于 \(F(2)\) 的方案是可以由两条路径拼起来的,不需要多做讨论
硬是不能感性理解的话,给出式子:
枚举两个点,要求: \(\sum\sum (x[j]-x[i]+y[j]-y[i]+1)*C_{x[j]-x[i]+y[j]-y[i]}^{x[j]-x[i]}*S_i*T_j\)
其中 \(S_i\) 表示点 \(i\) 达到第一个矩形内所有点的方案, \(T_i\) 表示 点 \(i\) 到第三个矩形的方案
然后我们发现 \(\sum_{j}C_{x[j]-x[i]+y[j]-y[i]}^{x[j]-x[i]}*T_j=T_i\)
那么原式子就跟 \(j\) 无关了,变为 \(\sum(x[i]+y[i]+1)*S_i*T_i-\sum(-x[j]-y[j])*S_j*T_j\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10,mod=1e9+7;
int x[10],y[10],Fac[N],inv[N];
inline void priwork(){
Fac[0]=inv[0]=inv[1]=1;
for(int i=1;i<N;i++)Fac[i]=1ll*Fac[i-1]*i%mod;
for(int i=2;i<N;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<N;i++)inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
inline void add(int &x,int y){x+=y;if(x<0)x=(x%mod+mod)%mod;if(x>=mod)x-=mod;}
inline int C(int a,int b){
return 1ll*Fac[a]*inv[b]%mod*inv[a-b]%mod;
}
inline int G(int x,int y){return C(x+y,x);}
inline int F(int x1,int y1,int x2,int y2){
return (1ll*G(x2+1,y2+1)-G(x1,y2+1)-G(x2+1,y1)+G(x1,y1))%mod;
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
priwork();
for(int i=1;i<=6;i++)cin>>x[i];
for(int i=1;i<=6;i++)cin>>y[i];
int ans=0;
for(int x1=x[3],y1=y[3];x1<=x[4];x1++)
add(ans,1ll*F(x1-x[2],y1-1-y[2],x1-x[1],y1-1-y[1])*F(x[5]-x1,y[5]-y1,x[6]-x1,y[6]-y1)%mod*(mod-x1-y1)%mod);
for(int x1=x[3],y1=y[3];y1<=y[4];y1++)
add(ans,1ll*F(x1-1-x[2],y1-y[2],x1-1-x[1],y1-y[1])*F(x[5]-x1,y[5]-y1,x[6]-x1,y[6]-y1)%mod*(mod-x1-y1)%mod);
for(int x2=x[3],y2=y[4];x2<=x[4];x2++)
add(ans,1ll*F(x2-x[2],y2-y[2],x2-x[1],y2-y[1])*F(x[5]-x2,y[5]-y2-1,x[6]-x2,y[6]-y2-1)%mod*(x2+y2+1)%mod);
for(int x2=x[4],y2=y[3];y2<=y[4];y2++)
add(ans,1ll*F(x2-x[2],y2-y[2],x2-x[1],y2-y[1])*F(x[5]-x2-1,y[5]-y2,x[6]-x2-1,y[6]-y2)%mod*(x2+y2+1)%mod);
cout<<ans<<endl;
return 0;
}