題意
有一個網格圖,給出 3 個矩形,後一個嚴格在前一個右上方。
在第 1 個矩形種選一個點做起點,在第 3 個矩形中選一個點做終點,求從起點走到終點,只能向右或者向上走,不能經過第 2 個矩形的方案數,對 998244353 取模。
第 1 和 3 個矩形邊長 l1,l3≤106 , 第 2 個矩形邊長 l2≤5∗104 ,所有點的座標 ∀x,y≤4.5∗108
思路
考慮容斥,先算出總方案數,再減去經過第 2 個矩形的方案數。
對於總方案數,是從一個矩陣走到另一個矩陣。對於經過第 2 個矩形的方案,必然會經過第 2 個矩形的下邊界或者左邊界,枚舉經過的是哪個點,轉化爲一個矩形走到一個點+一個點走到一個矩形的方案數。
那麼剩下的就是兩個問題:點走到矩形,矩形走到矩形。
首先點走到點,假如起點和終點橫座標和縱座標的差分別爲 x,y ,那麼方案數爲 Cx+yx 。
然後是點走到矩形,用點走到點來推。假設點和矩形的最左邊、最右邊橫座標差爲 x1,x2 , y1,y2 是縱座標差,那麼方案數爲: i=x1∑x2j=y1∑y2Ci+jj
把組合數列項(用遞推公式): Ci+jj=Ci+j+1j−Ci+jj−1
發現可以抵消很多,最後得到 j=y1∑y2Ci+jj=Ci+y2+1y2−Ci+y1y1−1
同理,方案數可以化爲:
i=x1∑x2j=y1∑y2Ci+jj=i=x1∑x2(Ci+y2+1y2−Ci+y1y1−1)=i=x1∑x2Cy2+i+1i+1−i=x1∑x2Cy1−1+i+1i+1=Cy2+x2+2x2+1−Cy2+x1+1x1−Cy1+x2+1x2+1+Cy1+x1x1
令 Δx=x2−x1,Δy=y2−y1 ,
令 ∑i=x1x2∑j=y1y2Ci+jj=D(x1,y1,Δx,Δy) ,那麼式子可以寫成:
D(x1,y1,Δx,Δy)=Cx1+y1+Δx+Δy+2x1+Δx+1−Cx1+y1+Δy+1x1−Cx1+y1+Δx+1x1+Δx+1+Cx1+y1x1
設第 1 個矩形的最右和最左分別距離第 3 個矩形的最左 x1,x2,第 1 個矩形的最上和最下分別距離第 3 個矩形的最下 y1,y2,第 3 個矩形橫軸和縱軸方向邊長分別爲 Δx,Δy ,那麼方案數表示如下:
i=x1∑x2j=y1∑y2D(i,j,Δx,Δy)
化簡!
===i=x1∑x2j=y1∑y2D(i,j,Δx,Δy)i=x1∑x2j=y1∑y2(Ci+j+Δx+Δy+2j+Δy+1−Ci+j+Δy+1j+Δy+1−Ci+j+Δx+1j+Ci+jj)i=x1+Δx+1∑x2+Δx+1j=y1+Δy+1∑y2+Δy+1Ci+jj−i=x1∑x2j=y1+Δy+1∑y2+Δy+1Ci+jj−i=x1+Δx+1∑x2+Δx+1j=y1∑y2Ci+jj+i=x1∑x2j=y1∑y2Ci+jjD(x1+Δx+1,y1+Δy+1,x2−x1,y2−y1)−D(x1,y1+Δy+1,x2−x1,y2−y1)−D(x1+Δx+1,y1,x2−x1,y2−y1)+D(x1,y1,x2−x1,y2−y1)
到這裏公式就已經推完了,複雜度是 O(l2∗k) , k 是求組合數的複雜度。
然後離解決這道題還差一步,在計算函數 D 中的組合數的時候,需要求很大的階乘。那我們分段打表,再用一個 unordered_map 就可以解決問題。表大概 104 左右就夠了。
所以這題就完美解決啦!
其實這題一直在反覆運用組合數的遞推公式,沒有什麼高端的東西,只要耐心總是推得出來的。
推式子真好玩!考場上你推一個看看(╯°Д°)╯( ┻━┻
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxN = 9e8, siz = 2e5, N = maxN/siz, mod = 998244353;
unordered_map<int, int> fac;
int iii, x[7], y[7], ans;
void add(int &x, int y){x += y; if (x >= mod) x -= mod;}
void init(){
for (int i = 0; i <= N; ++ i)
fac[i*siz] = FAC[i];
}
int fpow(int x, int y){
int ret = 1;
while (y){
if (y&1) ret = 1LL*ret*x%mod;
x = 1LL*x*x%mod;
y >>= 1;
}
return ret;
}
int Fac(int x){
if (fac.find(x) != fac.end()) return fac[x];
return fac[x] = 1LL*x*Fac(x-1)%mod;
}
int iFac(int x){
return fpow(Fac(x), mod-2);
}
int C(int x, int y){
if (x < 0 || y < 0 || x < y) return 0;
return 1LL*Fac(x)*iFac(y)%mod*iFac(x-y)%mod;
}
int D(int x, int y, int dx, int dy){
int ret = 0;
add(ret, C(x+y+dx+dy+2, x+dx+1));
add(ret, mod-C(x+y+dy+1, x));
add(ret, mod-C(x+y+dx+1, y));
add(ret, C(x+y, x));
return ret;
}
int solve(){
int x1 = x[5]-x[2], x2 = x[5]-x[1];
int y1 = y[5]-y[2], y2 = y[5]-y[1];
int dx = x[6]-x[5], dy = y[6]-y[5];
int ddx = x[2]-x[1], ddy = y[2]-y[1];
int ret = 0;
add(ret, D(x1+dx+1, y1+dy+1, ddx, ddy));
add(ret, mod-D(x1, y1+dy+1, ddx, ddy));
add(ret, mod-D(x1+dx+1, y1, ddx, ddy));
add(ret, D(x1, y1, ddx, ddy));
return ret;
}
int main()
{
init();
scanf("%d", &iii);
for (int i = 1; i <= 6; ++ i) scanf("%d", &x[i]);
for (int i = 1; i <= 6; ++ i) scanf("%d", &y[i]);
ans = solve();
for (int i = x[3]; i <= x[4]; ++ i)
add(ans, mod-1LL*D(i-x[2], y[3]-y[2]-1, x[2]-x[1], y[2]-y[1])*D(x[5]-i, y[5]-y[3], x[6]-x[5], y[6]-y[5])%mod);
for (int i = y[3]; i <= y[4]; ++ i)
add(ans, mod-1LL*D(x[3]-x[2]-1, i-y[2], x[2]-x[1], y[2]-y[1])*D(x[5]-x[3], y[5]-i, x[6]-x[5], y[6]-y[5])%mod);
printf("%d\n", ans);
return 0;
}