【学习笔记】[AGC018E] Sightseeing Plan

首先我们只会计算的情况是,给定起点和终点,计算从起点到终点的路径条数。由于起点和终点都是不确定的,因此显然不能直接计算。

我们考虑,从一个点走到一个矩形区域的方案数是多少呢?

F x , y F_{x,y} Fx,y表示从 ( 0 , 0 ) (0,0) (0,0)走到 ( x , y ) (x,y) (x,y)的方案数。那么我们有: F x , y = ∑ i = 0 y F x − 1 , i F_{x,y}=\sum_{i=0}^yF_{x-1,i} Fx,y=i=0yFx1,i

这个式子极具启发意义。它告诉我们,可以用到一个位置的方案数,来表示到一堆位置的方案数。这个思想非常重要。

于是我们得到了从 ( 0 , 0 ) (0,0) (0,0)到左下角为 ( a , b ) (a,b) (a,b),右上角为 ( c , d ) (c,d) (c,d)的矩形区域的方案数: F c + 1 , d + 1 − F a , d + 1 − F c + 1 , b + F a , b F_{c+1,d+1}-F_{a,d+1}-F_{c+1,b}+F_{a,b} Fc+1,d+1Fa,d+1Fc+1,b+Fa,b

那么问题转化为给定起点和终点,经过中间矩形区域其中一个点的方案数。我们枚举进入和出去中间矩阵的位置,那么能被选为中转点的数目就是这两个点的曼哈顿距离,注意到坐标是可以分离的,因此将贡献拆开计算即可。

复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
#define int ll
using namespace std;
const int mod=1e9+7;
const int N=2e6+5;
int X1,X2,X3,X4,X5,X6,Y1,Y2,Y3,Y4,Y5,Y6;
int fac[N],inv[N],res;
struct node{
	int X,Y,Z;
};
vector<node>v1,v2;
int fpow(int x,int y){
	int z(1);
	for(;y;y>>=1){
		if(y&1)z=z*x%mod;
		x=x*x%mod;
	}return z;
}
void init(int n){
	fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
	inv[n]=fpow(fac[n],mod-2);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
int binom(int x,int y){
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int G(int a,int b,int c,int d){
	assert(a<=c),assert(b<=d);
	return binom(c+d-a-b,c-a);
}
int solve(int A,int B,int C,int D){
	ll res=0;
	res=(res-G(A,B,X3,Y3)*(X3+Y3)%mod*G(X3,Y3,C,D)%mod)%mod;
	for(int i=X3+1;i<=X4;i++){
		res=(res-G(A,B,i,Y3-1)*(i+Y3)%mod*G(i,Y3,C,D)%mod)%mod;
	}
	for(int i=Y3+1;i<=Y4;i++){
		res=(res-G(A,B,X3-1,i)*(X3+i)%mod*G(X3,i,C,D)%mod)%mod;
	}res=(res+(X4+Y4+1)*G(X4,Y4,C,D)%mod*G(A,B,X4,Y4)%mod)%mod;
	for(int i=X4-1;i>=X3;i--){
		res=(res+G(i,Y4+1,C,D)*(i+Y4+1)%mod*G(A,B,i,Y4)%mod)%mod;
	}
	for(int i=Y4-1;i>=Y3;i--){
		res=(res+G(X4+1,i,C,D)*(X4+i+1)%mod*G(A,B,X4,i)%mod)%mod;
	}return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>X1>>X2>>X3>>X4>>X5>>X6>>Y1>>Y2>>Y3>>Y4>>Y5>>Y6,init(2000000);
	v1.pb({X1-1,Y1-1,1}),v1.pb({X1-1,Y2,-1}),v1.pb({X2,Y1-1,-1}),v1.pb({X2,Y2,1});
	v2.pb({X6+1,Y6+1,1}),v2.pb({X5,Y6+1,-1}),v2.pb({X6+1,Y5,-1}),v2.pb({X5,Y5,1});
    int res(0);
	for(int i=0;i<4;i++)for(int j=0;j<4;j++)res=(res+solve(v1[i].X,v1[i].Y,v2[j].X,v2[j].Y)*v1[i].Z*v2[j].Z)%mod;
	cout<<(res+mod)%mod;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值