【香蕉OI】NOTSET(組合數學)

文章目錄

題意

有一個網格圖,給出 3 個矩形,後一個嚴格在前一個右上方。

在第 1 個矩形種選一個點做起點,在第 3 個矩形中選一個點做終點,求從起點走到終點,只能向右或者向上走,不能經過第 2 個矩形的方案數,對 998244353998244353 取模。

第 1 和 3 個矩形邊長 l1,l3106l_1,l_3 \le10^6 , 第 2 個矩形邊長 l25104l_2\le 5*10^4 ,所有點的座標 x,y4.5108\forall x,y\le4.5*10^8

思路

考慮容斥,先算出總方案數,再減去經過第 2 個矩形的方案數。

對於總方案數,是從一個矩陣走到另一個矩陣。對於經過第 2 個矩形的方案,必然會經過第 2 個矩形的下邊界或者左邊界,枚舉經過的是哪個點,轉化爲一個矩形走到一個點+一個點走到一個矩形的方案數。

那麼剩下的就是兩個問題:點走到矩形,矩形走到矩形。

首先點走到點,假如起點和終點橫座標和縱座標的差分別爲 x,yx,y ,那麼方案數爲 Cx+yxC_{x+y}^{x}

然後是點走到矩形,用點走到點來推。假設點和矩形的最左邊、最右邊橫座標差爲 x1,x2x1,x2y1,y2y1,y2 是縱座標差,那麼方案數爲: i=x1x2j=y1y2Ci+jj\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}C_{i+j}^{j}

把組合數列項(用遞推公式): Ci+jj=Ci+j+1jCi+jj1C_{i+j}^{j}=C_{i+j+1}^{j}-C_{i+j}^{j-1}

發現可以抵消很多,最後得到 j=y1y2Ci+jj=Ci+y2+1y2Ci+y1y11\sum_{j=y_1}^{y_2}C_{i+j}^{j}=C_{i+y_2+1}^{y_2}-C_{i+y_1}^{y_1-1}

同理,方案數可以化爲:

i=x1x2j=y1y2Ci+jj=i=x1x2(Ci+y2+1y2Ci+y1y11)=i=x1x2Cy2+i+1i+1i=x1x2Cy11+i+1i+1=Cy2+x2+2x2+1Cy2+x1+1x1Cy1+x2+1x2+1+Cy1+x1x1\begin{aligned}\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}C_{i+j}^{j}&=\sum_{i=x_1}^{x_2}(C_{i+y_2+1}^{y_2}-C_{i+y_1}^{y_1-1}) \\&=\sum_{i=x_1}^{x_2}C_{y_2+i+1}^{i+1}-\sum_{i=x_1}^{x_2}C_{y_1-1+i+1}^{i+1} \\&=C_{y_2+x_2+2}^{x_2+1}-C_{y_2+x_1+1}^{x_1}-C_{y_1+x_2+1}^{x_2+1}+C_{y_1+x_1}^{x_1} \end{aligned}

Δx=x2x1,Δy=y2y1\Delta x=x_2-x_1,\Delta y=y_2-y_1
i=x1x2j=y1y2Ci+jj=D(x1,y1,Δx,Δy)\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}C_{i+j}^{j}=D(x_1, y_1, \Delta x,\Delta y) ,那麼式子可以寫成:

D(x1,y1,Δx,Δy)=Cx1+y1+Δx+Δy+2x1+Δx+1Cx1+y1+Δy+1x1Cx1+y1+Δx+1x1+Δx+1+Cx1+y1x1D(x_1, y_1, \Delta x,\Delta y)=C_{x_1+y_1+\Delta x+\Delta y+2}^{x_1+\Delta x+1}-C_{x_1+y_1+\Delta y+1}^{x_1}-C_{x_1+y_1+\Delta x+1}^{x_1+\Delta x+1}+C_{x_1+y_1}^{x_1}

設第 1 個矩形的最右和最左分別距離第 3 個矩形的最左 x1,x2x_1, x_2,第 1 個矩形的最上和最下分別距離第 3 個矩形的最下 y1,y2y_1, y_2,第 3 個矩形橫軸和縱軸方向邊長分別爲 Δx,Δy\Delta x,\Delta y ,那麼方案數表示如下:

i=x1x2j=y1y2D(i,j,Δx,Δy)\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}D(i,j,\Delta x,\Delta y)

化簡!

i=x1x2j=y1y2D(i,j,Δx,Δy)=i=x1x2j=y1y2(Ci+j+Δx+Δy+2j+Δy+1Ci+j+Δy+1j+Δy+1Ci+j+Δx+1j+Ci+jj)=i=x1+Δx+1x2+Δx+1j=y1+Δy+1y2+Δy+1Ci+jji=x1x2j=y1+Δy+1y2+Δy+1Ci+jji=x1+Δx+1x2+Δx+1j=y1y2Ci+jj+i=x1x2j=y1y2Ci+jj=D(x1+Δx+1,y1+Δy+1,x2x1,y2y1)D(x1,y1+Δy+1,x2x1,y2y1)D(x1+Δx+1,y1,x2x1,y2y1)+D(x1,y1,x2x1,y2y1) \begin{aligned} &\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}D(i,j,\Delta x,\Delta y) \\=&\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}(C_{i+j+\Delta x+\Delta y+2}^{j+\Delta y+1}-C_{i+j+\Delta y+1}^{j+\Delta y +1}-C_{i+j+\Delta x+1}^{j}+C_{i+j}^{j}) \\=&\sum_{i=x_1+\Delta x+1}^{x_2+\Delta x+1}\sum_{j=y_1+\Delta y+1}^{y_2+\Delta y+1}C_{i+j}^{j}-\sum_{i=x_1}^{x_2}\sum_{j=y_1+\Delta y+1}^{y_2+\Delta y+1}C_{i+j}^{j}-\sum_{i=x_1+\Delta x+1}^{x_2+\Delta x+1}\sum_{j=y_1}^{y_2}C_{i+j}^{j}+\sum_{i=x_1}^{x_2}\sum_{j=y_1}^{y_2}C_{i+j}^{j} \\=&D(x_1+\Delta x+1,y_1+\Delta y+1,x_2-x_1,y_2-y_1) \\&-D(x_1,y_1+\Delta y+1,x_2-x_1,y_2-y_1) \\&-D(x_1+\Delta x+1,y_1,x_2-x_1,y_2-y_1) \\&+D(x_1,y_1,x_2-x_1,y_2-y_1) \end{aligned}

到這裏公式就已經推完了,複雜度是 O(l2k)O(l_2*k)kk 是求組合數的複雜度。

然後離解決這道題還差一步,在計算函數 DD 中的組合數的時候,需要求很大的階乘。那我們分段打表,再用一個 unordered_map 就可以解決問題。表大概 10410^4 左右就夠了。

所以這題就完美解決啦!

其實這題一直在反覆運用組合數的遞推公式,沒有什麼高端的東西,只要耐心總是推得出來的。

推式子真好玩!考場上你推一個看看(╯°Д°)╯( ┻━┻

代碼

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxN = 9e8, siz = 2e5, N = maxN/siz, mod = 998244353;
// FAC[N+1] = {...} // 表太大了就不放了,自己打一下10s左右
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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章