2020.06.07日常總結

一個不大不小的日子——我終於註冊了 UVA 這麼個舉世文明的網站,並且答對了幾道題。(但是爲什麼 UVA 的註冊信息會跑到 QQ 郵箱的廢棄文件裏呢?

UVA11806 Cheerleaders\color{green}{\texttt{UVA11806 Cheerleaders}}

[Problem]\color{blue}{\texttt{[Problem]}}

在一個 n×mn \times m 的矩形網格里放 kk相同的石子,問有多少種合格的擺法。一共 TT 組輸入數據。

一個合格的擺法應該滿足一下條件:

  • 每個格子裏最多有 11 個石子。
  • 所有的石子都要用完。
  • 網格第一行、第一列、最後一行、最後一列都要有石子。

1T50,2n,m20,0k5001\leq T \leq 50,2 \leq n,m \leq 20,0 \leq k \leq 500

翻譯改自 《算法競賽入門經典》 一書。

[Solution]\color{blue}{\texttt{[Solution]}}

我們發現直接求有點難,怎麼辦呢?正難則反,我們考慮容斥。

記滿足“第一行沒有石子”的方案集爲 AA,“第一列沒有石子”的方案集爲 BB,“最後一行沒有石子”的方案集爲 CC,“最後一列沒有石子”的方案集爲 DD,全集爲 SS,則總的答案即爲“在 SS 但不在 A,B,C,DA,B,C,D 中任何一個集合中的元素”的個數。

把第一行、最後一行、第一列、最後一列有無石子的狀態存入一個二進制中(11 表示沒有,00 表示有),總的狀態可以通過枚舉得到。

考慮對於一個狀態如何求其方案數。我們發現,如果第一行或者最後一行沒有石子的話,我們可以把那一行刪除,即只剩下 n1n-1 個有用行(或者叫備選行)。對於列同理,注意當第一行和最後一行同時沒有石子時,就只剩下 n2n-2 個有用行了。

假設有 rr 個有用行,cc 個有用列,則總的方案數相當於是在 r×cr\times c 這麼多個格子選 kk 個格子的方案數,即:

(r×ck)=Cr×ck\binom {r \times c}{k}=C^{k}_{r \times c}

[code]\color{blue}{\texttt{[code]}}

const int mod=1e6+7;//模數 
int test_number,C[510][510];
int main(){
	scanf("%d",&test_number);
	for(int i=0;i<=500;i++){
		C[i][i]=C[i][0]=1;
		for(int j=1;j<i;j++)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	for(int T=1;T<=test_number;T++){
		register int n,m,k,sum=0;
		scanf("%d%d%d",&n,&m,&k);
		for(int S=0,r,c,b;S<16;S++){
			r=n;c=m;b=0;//初始化變量 
			if (S&1){r--;b++;}//第1行 
			if (S&2){r--;b++;}//第n行 
			if (S&4){c--;b++;}//第1列 
			if (S&8){c--;b++;}//第m列 
			if (b&1) sum=(sum-C[r*c][k]+mod)%mod;
			else sum=(sum+C[r*c][k])%mod;
		}
		printf("Case %d: %d\n",T,sum);
	}
	return 0;
}

容斥和正難則反的思維在實際中的應用是非常廣的,大家一定要注意,平時遇到一道直接計算很棘手的題目時,就可以想象可不可以用這兩種方法。

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