1. 程式人生 > 其它 >【題解】ABC231G - Balls in Boxes & H - Minimum Coloring

【題解】ABC231G - Balls in Boxes & H - Minimum Coloring

G 想到一個非常神奇的做法。

如果我們令第 \(i\) 個位置放入了 \(b_i\) 個球,那麼總代價一定是 \(\prod(a_i + b_i)\)

總方案數是 \(n^k\),我們只用求所有方案的代價之和。

對於一個代價,我們將它拆開,組合意義等價於選出一些 \(a_i\),剩下的選 \(b_i\),乘起來然後求和。

由於 \(a\) 是固定的,我們先簡單 DP 一下求得 \(f_{n,i}\) 表示從 \(n\) 個數中,選出 \(i\) 個數的乘積的和,直接做是 \(\mathcal{O}(N^2)\) 已經足夠了。

接下來我們列舉選出了 $x $ 個 \(b_i\),它們的和為 \(s(s\le k)\)

那麼答案為 \(\sum\limits_{x = 0}^n f_{n, x}\sum\limits_{s = 0}^k \binom{k}{s}(n - x)^{k - s}g_{x, s}\)

其中 \(g_{n, k}\) 表示有 \(n\) 個空盒子,進行 \(k\) 次操作,所有情況的代價之和。

顯然 \(g_{1, k} = k\)\(g_{n,k} = \sum\limits_{i = 0}^{k}i\binom{k}{i}g_{n - 1,k -i}\)。經過遞推和組合恆等變換後可以求得 \(g_{n,k} = \dfrac{k!n^{k - n}}{(k - n)!}\)

(這個式子可以在變形後歸納證明,不知道有沒有簡單的組合意義解法

我們將這個式子帶回去,可以求得 \(\sum\limits_{s = 0}^k \binom{k}{s}(n - x)^{k - s}g_{x, s} = \dfrac{k!n^{k - x}}{(k - x)!}\)

這式子可以直接 \(\mathcal{O}(N)\) 求,時間複雜度 \(\mathcal{O}(N^2)\)

#define N 1005
int n, a[N], f[N][N], k;
int g(int x){
	int cur = Pow(n, k - x);
	rep(i, 1, x)cur = 1LL * cur * (k - i + 1) % P;
	return cur;
}
int main(){
	n = read(), k = read();
	rep(i, 1, n)a[i] = read();
	f[0][0] = 1;
	rep(i, 1, n)rep(j, 0, n - 1)
		ad(f[i][j], f[i - 1][j]), ad(f[i][j + 1], f[i - 1][j] * 1LL * a[i] % P);
	int ans = 0;
	rep(i, 0, n)ad(ans, g(i) * 1LL * f[n][n - i] % P);
	ans = 1LL * ans * Pow(n, -k) % P;
	printf("%d\n", ans);
	return 0;
}

H 題很有意思,可惜沒想出來。

顯然這個個行列二分圖模型,對於一個可以染色的格子對應一條權值為 \(c_i\) 的邊,然後求二分圖的帶權最小邊覆蓋。

大家只知道最小邊覆蓋可以直接求最大匹配,考慮擴充套件到帶權圖。

對於最有情況,我們將選擇的邊扣出來,然後對這個邊集的生成子圖跑出最大匹配,顯然不在匹配中的點,它也要被覆蓋,它一定會選擇相鄰的最小的邊去覆蓋它。

所以對於每個點我們求相鄰的邊的最小值 \(v_i\),對於一個格子 \((a,b,c)\),連一條行 \(a\) 到列 \(b\), 權值為容量為 \(1\),權值為 \(v_a+v_b-c\) 的邊,然後跑最大費用流即可。答案就是最大 \(\sum v - ans\)

注意這裡的最大費用流是指費用最大的可行流而不是最大流,由於每條邊容量最大為 \(1\),我們只增廣代價為正的路徑即可。

感覺這個模型非常類似反悔貪心,不知道能不能擴充套件。

#define N 2005
#define int long long
int n, m, T, a[N], b[N], c[N], h[N], tot = 1, u[N];
struct edge{int to, nxt, val, cap;}e[N << 5];
void add(int x,int y,int z,int val){
	e[++tot].nxt = h[x], h[x] = tot, e[tot].to = y, e[tot].cap = z, e[tot].val = val;
	e[++tot].nxt = h[y], h[y] = tot, e[tot].to = x, e[tot].cap = 0, e[tot].val = -val;
}
int flow[N], pre[N], d[N], v[N], s, t, ans;
queue<int>q;
bool bfs(){
	memset(d, 0xcf, sizeof(d));
	q.push(s), d[s] = flow[t] = 0, flow[s] = 1;
	while(!q.empty()){
		int x = q.front();
		q.pop(), v[x] = 0;
		for(int i = h[x]; i; i = e[i].nxt)if(e[i].cap && d[x] + e[i].val > d[e[i].to]){
			d[e[i].to] = d[x] + e[i].val, flow[e[i].to] = 1, pre[e[i].to] = i;
			v[e[i].to] = 1, q.push(e[i].to);
		}
	}
	return d[t] > 0;
}
void updata(){
	ans -= d[t];
	int x = t;
	while(x != s){
		e[pre[x]].cap --;
		e[pre[x] ^ 1].cap ++;
		x = e[pre[x] ^ 1].to;
	} 
}
signed main(){
	n = read(), m = read(), T = read();
	memset(u, 0x3f, sizeof(u));
	rp(i, T){
		a[i] = read(), b[i] = read(), c[i] = read();
		cmn(u[a[i]], c[i]), cmn(u[b[i] + n], c[i]);
	}
	s = n + m + 1, t = s + 1;
	rp(i, n)add(s, i, 1, 0);
	rp(i, m)add(i + n, t, 1, 0);
	rp(i, n + m)ans += u[i];
	rp(i, T)add(a[i], b[i] + n, 1, u[a[i]] + u[b[i] + n] - c[i]);
	while(bfs())updata();
	printf("%lld\n", ans);
	return 0;
}