AtCoder Grand Contest 018 E

Description

给出平面上坐标单调不降的三个矩形 (A,B,C) ,你需要在 (A) 选择一个起点, (B) 选择一个位置休息, (C) 选择一个终点,期间你可以向上和向右走
求所有选择的方案和
题面

Solution

写起来有点恶心
先考虑一个简单的问题,求 从 ((0,0)) 出发,走到矩形 ((0,0),(x,y)) 内任意一点的方案数

[sum_{i=0}^{x}sum_{j=0}^{y}C_{i+j}^{i} ]

用杨辉三角合并得到:

[sum_{i=0}^{x}C_{i+y+1}^{i+1} ]

再用杨辉三角合并成:

[C_{x+y+2}^{x+1}-1 ]

这个合并的过程可以抽象的理解为杨辉三角的合并,也可以分析组合意义:
(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)) 的方案是可以由两条路径拼起来的,不需要多做讨论
硬是不能感性理解的话,给出式子:
枚举两个点,要求: (sumsum (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;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8717898.html