算法分析
规则后的图形
题目给定的图片是不规则图形,我们可以将它分解成若干个规则图形,因为规则图形可以通过公式直接计算出来,如上图所示,对于某个规则的矩形,长度是a
,宽度是b
,放k
辆车(k <= b <= a
),在长度是a
行中任选k
行进行摆放,共有b
列,第一辆车有b
种摆放方案,第二辆车有b - 1
中摆放方案…,第k
辆车有b - k + 1
种摆放方案,因此一共有\(\displaystyle C_a^kP_b^k\)种摆放方案
您是对\(C_b^iP_a^i\)中的\(P_a^i\)有疑问吗,最开始我认为\(P_a^i\)应该是\(C_a^i\),后来发现想错的点是因为我认为是从
a
列里面随便选i
列即可,不需要顺序,所以是\(C_a^i\),但是在每一列里面,车究竟在哪一行,是没有考虑到的,所以我们应该在选好了用哪些行以后,针对每一行来看安排在哪一列,若矩阵是\(3*4\)的,我们选择了第一行和第二行,然后我们需要在第一行选择一列,有四种选择,在第二行,有三种选择,所以是\(P_4^2\).也就是说\((1,3),(2,4)与(1,4),(2,3)\)是不同的选择
分解图形(按照红线位置划分成两个矩形)
上半部分摆放i
辆车,有\(\displaystyle C_b^iP_a^i\)种摆放方案
下半部分摆放k - i
辆车,由于上半部分摆放了i
辆车,占用的列对下半部分选择有了一定的影响,即占用的列对于下半部分是不可使用的,因此从d
行中选k - i
行,有a + c - i
列可以进行摆放并摆放k - i
辆车,因此有 \(C_d^{k-i}P_{a+c-i}^{k-i}\) 种摆放方案
由于上半部分的摆放情况 和 下半部分的摆放情况是相互独立的,因此可以使用乘法原理,上半部分摆放i
辆车,整张图的摆放方案是
时间复杂度 \(O(Nlogmod)\)
\(N = 2000\),\(mod = 100003\)
实现代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2010, mod = 100003; //因为式子中出现a+c,所以N要开两倍
int fact[N], infact[N]; //阶乘与阶乘的逆元
//快速幂模板
int qmi(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
//组合数
int C(int a, int b) {
if (a < b) return 0;
// C(a,b)=a!/(a-b)! % mod
return (LL)fact[a] * infact[a - b] % mod * infact[b] % mod;
}
//排列数
int P(int a, int b) {
if (a < b) return 0;
// P(a,b)=a!/(a-b)!
return (LL)fact[a] * infact[a - b] % mod;
}
int main() {
//组合数公式II
//预处理出阶乘和阶乘的逆元
fact[0] = infact[0] = 1; // 0的阶乘是1,这是人为的规定。 1/1也是1,infact[0]也是1
for (int i = 1; i < N; i++) {
fact[i] = (LL)fact[i - 1] * i % mod; //阶乘
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod; //因为100003是质数,可以用费马小定理求出阶乘的逆元
}
int a, b, c, d, k;
cin >> a >> b >> c >> d >> k;
int res = 0;
for (int i = 0; i <= k; i++) //在上面的矩阵中,放i个,但要注意C(a,b),P(a,b)中a>=b,这里处理的也非常巧妙,
//没有在计算时进行特判,而是在实现C函数、P函数时进行了特判,if(a<b) return 0;
res = (res + (LL)C(b, i) * P(a, i) % mod * C(d, k - i) % mod * P(a + c - i, k - i)) % mod;
cout << res << endl;
return 0;
}