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\) 就可以看作是

\[\sum_{i=0}^{l_2-l_1+1} f(i)\times(-1)^i \]

原因是我们的答案是至多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
*/
posted @ 2022-03-29 15:19  RevolutionBP  阅读(71)  评论(1编辑  收藏  举报