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;
}