Atcoder 的题目就是好 思维难度高到我都不会
题目描述
在网格图上,给 (X_1 leq X_2 < X_3 leq X_4 < X_5 leq X_6)和(Y_1 leq Y_2 < Y_3 leq Y_4 < Y_5 leq Y_6),需要选择 ((p,q) in [X_1,X_2] imes[Y_1,Y_2],(s,t) in [X_3,X_4] imes[Y_3,Y_4],(u,v)in [X_5,X_6] imes[Y_5,Y_6]),以及一条从 ((p,q)) 经过 ((s,t)) 到达 ((u,v)) 的只能向上或向右的路径。两个方案不同当且仅当 ((p,q),(s,t),(u,v)) 选择不同或者路径不同。求方案数
(X,Y leq 10^6)
题解
我们先分成几个小问题解决:
点到点的方案数
((0,0) o (x,y)):方案数显然是(inom {x+y}{x}) 。
点到面的方案数
先看最简单的情形:((0,0) o [0,X] imes [0,Y])
我们先注意到对于组合数的上下指标求和 有这两种形式:
大概都是用组合数的简单递推形式搞。
如果是 ((0,0) o [X_1,X_2] imes[Y_1,Y_2])呢?只需要用二维前缀和的思想就好了。
面到面
考虑点到面的情况实际上可以拆成点到四个点的方案数(带权)。所以面到面可以转化为 (16) 对点到点。
这个题
暴力的想法是把第一个和第三个面化为 (4) 个点 那么枚举 (16) 种可能 再枚举中间的点 就可以计算。
考虑我们先随便确定一条经过中间面的路径 发现这个路径对答案的贡献就是和中间面交的长度。
考虑进入的点是 ((x,y)) 出去的点是 ((a,b)) 那么贡献 (a-x+b-y+1) 所以就可以变成枚举每一个入点 考虑这个入点方案会被计算多少次(强制路径经过这个点的方案) 然后有个系数 (-x-y) 出点类似 系数是 (a+b+1)
不理解可以看看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 5e6 + 5;
const int ha = 1e9 + 7;
inline int qpow(int a,int n=ha-2){
int res = 1;
while(n){
if(n & 1) res = 1ll*res*a%ha;
a = 1ll*a*a%ha;
n >>= 1;
}
return res;
}
int fac[MAXN],inv[MAXN];
inline void prework(){
fac[0] = 1;FOR(i,1,MAXN-1) fac[i] = 1ll*fac[i-1]*i%ha;
inv[MAXN-1] = qpow(fac[MAXN-1]);
ROF(i,MAXN-2,0) inv[i] = 1ll*inv[i+1]*(i+1)%ha;
}
inline int C(int n,int m){
if(n < m) return 0;
if(!m) return 1;
return 1ll*fac[n]*inv[m]%ha*inv[n-m]%ha;
}
inline int calc1(int x,int y){// (0,0) 到 (x,y)
if(x < 0 || y < 0) return 0;
return C(x+y,x);
}
inline int calc2(int x,int y){ // (0,0) 到 ([0,x],[0,y])
return (calc1(x+1,y+1)+ha-1)%ha;
}
inline int calc3(int x1,int x2,int y1,int y2){// (0,0) 到 ([x1,x2],[y1,y2])
int res = calc1(x2+1,y2+1);
(res += ha-calc1(x2+1,y1)) %= ha;
(res += ha-calc1(x1,y2+1)) %= ha;
(res += calc1(x1,y1)) %= ha;
return res;
}
inline int calc4(int x,int y,int x1,int x2,int y1,int y2){// (x,y) 到 ([x1,x2],[y1,y2])
return std::min(x1,x2) >= x && std::min(y1,y2) >= y ? calc3(x1-x,x2-x,y1-y,y2-y) : calc3(x-x2,x-x1,y-y2,y-y1);
// 实际应用是要判包含的 但是这个题不会
}
int x[233],y[233];
int main(){
prework();
FOR(i,1,6) scanf("%d",x+i);
FOR(i,1,6) scanf("%d",y+i);
int ans = 0;
FOR(i,x[3],x[4]){
(ans += 1ll*(ha-(i+y[3])%ha)*calc4(i,y[3]-1,x[1],x[2],y[1],y[2])%ha*calc4(i,y[3],x[5],x[6],y[5],y[6])%ha) %= ha;
(ans += 1ll*(i+y[4]+1)%ha*calc4(i,y[4],x[1],x[2],y[1],y[2])%ha*calc4(i,y[4]+1,x[5],x[6],y[5],y[6])%ha) %= ha;
}
FOR(i,y[3],y[4]){
(ans += 1ll*(ha-(i+x[3])%ha)*calc4(x[3]-1,i,x[1],x[2],y[1],y[2])%ha*calc4(x[3],i,x[5],x[6],y[5],y[6])%ha) %= ha;
(ans += 1ll*(x[4]+i+1)%ha*calc4(x[4],i,x[1],x[2],y[1],y[2])%ha*calc4(x[4]+1,i,x[5],x[6],y[5],y[6])%ha) %= ha;
}
printf("%d
",ans);
return 0;
}