1. 程式人生 > >清北學堂模擬賽d3t3 c

清北學堂模擬賽d3t3 c

後乘 方法 pre 容斥 else cst res 藍色 很難

技術分享

分析:一開始拿到這道題真的是無從下手,暴力都很難打出來.但是基本的方向還是要有的,題目問的是方案數,dp不行就考慮數學方法.接下來比較難想.其實對於每一行或者每一列,我們任意打亂順序其實對答案是沒有影響的.那麽我們按照高度從大到小對行和列進行排序,單獨考慮所有高度相等的行和列,組成了一個L形,如果我們把所有的L形的方案數求出來最後乘起來就是答案了,關鍵就是怎麽求它的方案數.

要求L形中滿足每行每列最大高度不超過H的方案數很難求,因為不好保證最大高度,正難則反,先求出不滿足的,但是不滿足的也比較難求,我們就先求出有一行不滿足的,一列不滿足的,然後求出兩行不滿足的,兩列不滿足的,這其實就是一個容斥原理,處於限制的行和列由於取的數小於H,所以每一位能取H個數,而沒有限制的可以取0~H,共H+1個數,那麽方案數就出來了:H^(限制的面積) + (H+1)^(沒有限制的面積)* (-1)^|S|,就像下面一個圖:技術分享

藍色部分沒有限制,黑色部分有限制,黑色部分和藍色部分組成了一個L形.

正難則反,如果求滿足某某條件很難,就求出不滿足某某條件的,如果還是很難,就分解一下,利用容斥原理來做.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int mod = 1e9 + 9;
long long n, m,a[10010],b[10010],x,y;
long long ans = 1,c[100][100];

void init() { c[0][0] = 1; for (int i = 1; i <= 90; i++) { c[i][0] = 1; for (int j = 1; j <= 90; j++) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod; } } long long qpow(long long a, long long b) { long long res = 1; while (b) { if (b & 1
) res = (res * a) % mod; b >>= 1; a = (a * a) % mod; } return res; } long long cal(long long x, long long y, long long nx, long long ny, int p) { long long res = 0; for (long long i = 0; i <= nx; i++) for (long long j = 0; j <= ny; j++) { long long temp = qpow(p, x * y - (x - i) * (y - j)) * qpow(p + 1, (x - i) * (y - j) - (x - nx) * (y - ny)) % mod * c[nx][i] % mod * c[ny][j] % mod; if ((i + j) & 1) res = ((res - temp) % mod + mod) % mod; else { res += temp; while (res >= mod) res -= mod; } } return res; } int main() { scanf("%lld%lld", &n, &m); for (int i = 1; i <= n; i++) { long long x; scanf("%lld", &x); a[x]++; } for (int i = 1; i <= m; i++) { long long x; scanf("%lld", &x); b[x]++; } init(); for (int i = 10000; i >= 0; i--) if (a[i] || b[i]) { x += a[i]; y += b[i]; ans = ans * cal(x, y, a[i], b[i],i) % mod; } printf("%lld\n", ans); return 0; }

清北學堂模擬賽d3t3 c