3.29考试总结
T2
给你正视图和左视图,求方案数
首先我们考虑对于一个正视图来说我们可以得知哪些信息?
肯定是这一列的最大值,还有就是哪些列有方块,本质上还是前面的那个
对于一个左视图呢?
答案是同理的
所以我们看俯视图的时候,如果我们用头顶标数法,然后从上往下数,三层标上三,二层标上二,那么我们就可以得到:一个 \(n\times m\) 的矩阵,上面的每一个位置填着一个自然数
对于我们刚才左视图和正视图的限制,发现其实就变成了每一行的最大值或者每一列的最大值的限制
因此题意就转化成了给你一个矩阵每行每列的最大值限制,要求每一行的最大值为 \(a_i\),每一列的最大值为 \(b_i\),问你组成这个矩阵有多少种填数方案
首先我们考虑排序是不破坏原来的图的完整性的
然后考虑对于一个矩阵来说,让它两侧都按照降序排序,那么就会有一个这样的矩阵:
5 4 2 2 1
5 5 4 2 2 1
4 4 4 2 2 1
4 4 4 2 2 1
2 2 2 2 2 1
2 2 2 2 2 1
1 1 1 1 1 1
很显然每个位置的最大值我们是知道的,此时考虑一个问题:为啥会有很多种方案?
发现其实就是这一列或者这一行已经填上最大值了,你发现最大值的限制对你就没影响了,只要你能选出一个不超过最大值的数都是可以的。
其实到这里,我们可以直接把所有相等的数放在一起处理,原因如下:
蓝色的是行,红色的是列,我们考虑,最左上角肯定得填 5
那么填了5以后,是不是从最上面开始数第一行第二列的那个四在 \(x\) 轴方向上就解放了,同时第二行第一列的那个数在 \(y\) 轴方向上也就解放了所以两者一起考虑的话那么我们考虑 三个4 的时候,是不是只要满足第二行是够的和第二列是满足 \(\max\) 即可,因为别的其实是已经有人为我们负重前行了,而且由于是降序排列,所以这个四必须把第二列和第二行填满。
同理我们考虑到3的时候,也可以这样理解,我们已经填满了第一二列,一二行,所以三只需要填满第三四五列和三四五行即可
而且我们发现对于每一堆相等的数,它们肯定是长成 \(\rfloor\) 这样的。
所以我们接下来只需要考虑我们如何满足这个 \(\rfloor\) 形即可
我们再画一个图
对于这个图来说我们应该怎么做?
此时考虑容斥,首先建设这些点的最大值是 \(a\),我们假设 \(r_1\) 到 \(r_2\) 这几行是合法的,那么只需要考虑 \(l_1\) 到 \(l_2\) 这几列,假设这几列里面至少有 \(i\) 列不合法,设 \(f(i)\) 表示此时的方案数,那么 \(ans\) 就可以看作是
原因是我们的答案是至多0列不合法,对于 \(r_1\) 到 \(r_2\) 反正都是合法的,只需要上面的几列容斥一下就好了
这时候考虑怎么算 \(f(i)\)
首先钦定 \(i\) 列不合法,那么就会有 $\displaystyle \binom {l_2 - l_1 + 1} {i} $ 种选择方案,每种方案内部的每一个点都会有 \([0,a-1]\) 种放置方案,也就是 \(a\) 种选择方案
所以总共的贡献就是 \(\displaystyle \binom {len} {i} \times a^{i\times r_2}\)
再画一个图:
然后我们考虑由于 \(r_1\) 到 \(r_2\) 行是合法的,那么可供我们选择的就会有 \(l_2 - i\) 列,每一行会有 \((a+1)^{l_2-i} - a^{l_2-i}\) 种方案,一共就会有 \(((a+1)^{l_2-i} - a^{l_2-i})^{r_2-r_1+1}\) 种方案
考虑完上述限制后,还剩下的点是这些:
这些点爱咋填咋填,也就是 \((a+1)^{(len-i)\times (r_1-1)}\) 种方案
这些要求是要同时满足的,也就是乘法原理拼起来,所以上述的三种情况全部乘起来就是最后的 \(f(i)\) 了
因此 \(f(i)= \displaystyle (a+1)^{(len-i)\times (r_1-1)}\times \binom {len} {i} \times a^{i\times r_2}\times ((a+1)^{l_2-i} - a^{l_2-i})^{r_2-r_1+1}\)
所以我们直接每次找到一段水平和竖直方向上相同的数,直接求解即可
/*
BLACKPINK in your area
BLACKPINK is the Revolution
light up the sky
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <vector>
#include <queue>
#include <map>
#define int long long
using namespace std;
using ll = long long;
using P = pair <int, int>;
namespace scan {
template <typename T>
inline void read(T &x) {
x = 0; int f = 0; char c = getchar();
for (; !isdigit(c); c = getchar()) f |= (c == '-');
for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
if (f) x = -x;
}
template <typename T, typename ...Args>
inline void read(T &x, Args &...args) {
read(x), read(args...);
}
template <typename T>
inline void write(T x, char ch) {
if (x < 0) x = -x, putchar('-');
static short st[40], tp;
do st[++tp] = x % 10, x /= 10; while (x);
while (tp) putchar(st[tp--] | 48);
putchar(ch);
}
template <typename T, typename ...Args>
inline void write(T x, char ch, Args ...args) {
write(x, ch), write(args...);
}
}
using namespace scan;
const int N = 1e5 + 5;
const int mod = 1e9 + 9;
#define rep(i, l, r) for (int i = l; (i) <= (r); ++(i))
#define per(i, r, l) for (int i = r; (i) >= (l); --(i))
int n, m;
int a[N], b[N], p[N];
ll fac[N], ifac[N], ans = 1;
ll l, l1, l2, r, r1, r2, cnt;
inline ll qpow(ll x, int k, ll res = 1) {
while (k) {
if (k & 1) res = res * x % mod;
x = x * x % mod;
k >>= 1;
}
return res;
}
inline void init() {
fac[0] = fac[1] = 1;
rep (i, 2, N - 1) fac[i] = fac[i - 1] * i % mod;
ifac[N - 1] = qpow(fac[N - 1], mod - 2);
per (i, N - 2, 0) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
inline ll binom (ll x, ll y) {return fac[x] * ifac[y] % mod * ifac[x - y] % mod;}
// = (ll)c(a, i) * ksm(s, (ll)i * rb % (mod - 1)) % mod *
// ksm(sub(ksm(s + 1, ra - i), ksm(s, ra - i)), b) % mod *
// ksm(s + 1, (ll)(a - i) * lb % (mod - 1)) % mod;
inline ll f(ll l1, ll l2, ll r1, ll r2, ll A, ll i) {
ll len = l2 - l1 + 1, len2 = r2 - r1 + 1;
ll res = 1;
res = res * binom (len, i) % mod * qpow(A, i * r2) % mod;
res = res * (qpow((qpow(A + 1, l2 - i) - qpow(A, l2 - i) + mod) % mod, len2)) % mod;
res = res * qpow(A + 1, (len - i) * (r1 - 1)) % mod;
return res;
}
inline ll solve(int l1, int l2, int r1, int r2, int A) {
ll len = l2 - l1 + 1;
ll res = 0;
rep (i, 0, len)
res = (res + (i & 1 ? -1 : 1) * f(l1, l2, r1, r2, A, i) + mod) % mod;
return res;
}
namespace RevolutionBP {
inline void main() {
init();
// freopen("1.in", "r", stdin);
// cout << fac[6] << endl;
// cout << ifac[6] << endl;
// cout << qpow(fac[6], mod - 2) << endl;
// cout << binom (6, 3) << endl;
read(n);
rep (i, 1, n) read(a[i]), p[++cnt] = a[i];
read(m);
rep (i, 1, m) read(b[i]), p[++cnt] = b[i];
sort (a + 1, a + 1 + n);
sort (b + 1, b + 1 + m);
if (a[n] != b[m]) return puts("0"), void();
reverse (a + 1, a + 1 + n);
reverse (b + 1, b + 1 + m);
sort (p + 1, p + 1 + cnt);
int len = unique (p + 1, p + 1 + cnt) - p - 1;
reverse(p + 1, p + 1 + len);
rep (i, 1, len) {
int l2 = l1, r2 = r1;
while (l2 + 1 <= n && a[l2 + 1] == p[i]) ++ l2;
while (r2 + 1 <= m && b[r2 + 1] == p[i]) ++ r2;
ans = ans * solve(l1 + 1, l2, r1 + 1, r2, p[i]) % mod;
// cout<< "l,r: ";
// write(l1 + 1, ' ', l2, ' ', r1 + 1, ' ', r2, '\n');
// cout << "solve: ";
// cout << solve(l1 + 1, l2, r1 + 1, r2, p[i]) % mod << endl;
l1 = l2, r1 = r2;
// cout << endl;
}
write(ans, '\n');
return void();
}
}
signed main() {
RevolutionBP::main();
return 0;
}
// write: RevolutionBP
/*
20
329 48 148 186 252 47 69 159 44 54 245 297 62 228 24 109 57 84 329 70
12
329 59 306 102 108 226 6 313 126 39 138 200
861282335
*/