AGC049E Increment Decrement
想了一个比较菜的做法。
认为操作是我们选择一个区间 \([l,r]\),区间加或减 \(1\),代价是 \(\min(r-l+1,C)\)。分析一下操作的性质,可以发现操作的区间只有包含或不交。因为如果操作了 \([a,c]\) 和 \([b,d]\) 且 \(a\le b\le c\le d\),如果一正一负那么公共的部分不用操作,两个正改成 \([a,d]\) 和 \([b,c]\) 不劣。
考虑差分,记 \(d_i=a_i-a_{i-1}\),那么就相当于提供了 \(d_i\) 个 \(+\) 或者 \(-d_i\) 个 \(-\)。然后 \(+\) 和 \(-\) 要匹配,对于 \(i\) 和 \(j\) 位置的匹配代价是 \(\min(|j-i|,C)\)。
根据 unr d1t2 的想法,分层,每层 \(+-\) 交替。这里的分层就是对 \(a\) 转 \(01\),然后 \(0\to 1\) 就是 \(+\),\(1\to 0\) 是 \(-\),其余是空位。考虑怎么算最小权匹配,把 \(|j-i|\) 和 \(C\) 分开考虑,我们可以操作若干 \((i,j)\),剩下的全部用 \(C\) 匹配。因此可以看成选择 \((i,j)\) 的代价是 \(|j-i|-C\)。
显然此时 \(i,j\) 是相邻的,于是可以 dp。记 \(f_i\) 为前 \(i\) 个点答案,那么有 \(f_i=\min(f_{i-1},f_{i-2}+dis_i-C)\)。这里 \(dis\) 为第 \(i\) 个点和上一个位置的距离。
实际上原题就可以做 \(\mathrm{poly}(n)\) 了,不过 \(f\) 是 \(O(n^2)\) 的,然后还要转 \(01\),复杂度非常高。
考虑继续优化。先把 \(f\) 取相反数,于是有 \(f_i=\max(f_{i-1},f_{i-2}+C-dis_i)\)。可以变成 \(f_i-f_{i-1}=\max(0,f_{i-2}-f_{i-1}+C-dis_i)\),那么我们只要维护 \(f_i-f_{i-1}\) 即可,这是 \(O(n)\) 的。
那么对于原题,先转 \(01\),此时记 \(F_{i,j,d,0/1}\) 表示前 \(i\) 个位置,\(f\) 的差是 \(j\),当前 \(dis=d\),最后一个位置是 \(0/1\) 的方案数。每次如果更新了 \(j\) 就算一下贡献。这样复杂度 \(O(n^5)\)。
但是可以发现同时记录 \(j,d\) 是没用的,我们只关心 \(C-j-d\),于是只要记录这个值转移即可 \(O(n^4)\)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int mod = 1e9 + 7;
void Add(int &x, ll y) { x = (x + y) % mod; }
const int kN = 55;
int n, c, k;
int pwk[kN];
int b[kN][kN];
int t[kN * kN];
int cnt[kN][2];
int f[kN][kN][2];
int g[kN][kN][2];
int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> c >> k;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= k; j++) {
cin >> b[i][j];
t[(i - 1) * k + j] = b[i][j];
}
}
pwk[0] = 1;
for(int i = 1; i <= n; i++) {
pwk[i] = (ll)pwk[i - 1] * k % mod;
}
int res = 0;
sort(t + 1, t + n * k + 1);
for(int L = 0; L < n * k; L++) {
if(t[L] == t[L + 1]) continue;
for(int i = 1; i <= n; i++) {
cnt[i][0] = cnt[i][1] = 0;
for(int j = 1; j <= k; j++) {
cnt[i][b[i][j] > t[L]]++;
}
}
cnt[n + 1][0] = 1;
memset(f, 0, sizeof(f));
for(int i = 1, prd = 1; i <= n; i++) {
f[i][c - 1][1] = (ll)prd * cnt[i][1] % mod;
prd = (ll)prd * cnt[i][0] % mod;
}
int sum = 0;
for(int i = 1; i <= n; i++) {
for(int j = 0; j < c; j++) {
for(int v : {0, 1}) {
int val = f[i][j][v];
if(!val) continue;
Add(f[i + 1][max(j - 1, 0)][v], (ll)val * cnt[i + 1][v]);
Add(f[i + 1][c - 1 - j][v ^ 1], (ll)val * cnt[i + 1][v ^ 1]);
int add = (ll)val * cnt[i + 1][v ^ 1] % mod;
add = (ll)add * j % mod;
add = (ll)add * pwk[max(n - i - 1, 0)] % mod;
Add(sum, mod - add);
}
}
}
memset(g, 0, sizeof(g));
g[0][0][0] = 1;
for(int i = 0; i <= n; i++) {
for(int c = 0; c <= i; c++) {
for(int v : {0, 1}) {
int val = g[i][c][v];
if(!val) continue;
Add(g[i + 1][c][v], (ll)val * cnt[i + 1][v]);
Add(g[i + 1][c + 1][v ^ 1], (ll)val * cnt[i + 1][v ^ 1]);
}
}
}
for(int i = 0; i <= n + 1; i += 2) {
Add(sum, (ll)g[n + 1][i][0] * (i / 2 * c));
}
Add(res, (ll)(t[L + 1] - t[L]) * sum);
}
cout << res << "\n";
return 0;
}
浙公网安备 33010602011771号