题解:CF2061I Kevin and Nivek
题意:给出 \(n\) 场比赛,有两种比赛:
-
A 花费 \(a_i\) 的代价可以获胜,否则 B 胜。
-
比较前面 \(i-1\) 场比赛中谁胜的更多,则该场比赛谁胜,特别的,如果两者赢的场数一样,则 A 胜。
求对于 \(k=0,1\cdots n\),A 要赢下 \(k\) 场比赛的最小代价。
做法:
直接做可以轻松做到 \(n^2\),定义 \(dp_{i,j}\) 为前 \(i\) 场赢了 \(j\) 次,直接 dp 就好。
接下来就相当困难了,这个第二种比赛非常难以转移。
先思考如果没有第二种比赛,那么贪心取就可以了。
我们这么考虑,进行分治,我们考虑将 \(l\) 处的转移到 \(r\) 处。这样转移看似没有什么意义,但是我们可以发现一件事情,如果 $|j-(l-j)| > r - l $,那么要不然全赢要不然全输,这些状态就可以直接只考虑第一种比赛贪心取,这个可以当成是一个凸包和一个非凸包做 \((min,+)\) 卷积。那么对于剩余的状态,我们就递归让他去 \([l,mid]\) 和 \([mid,r]\) 转移。这样每次下传的状态数只有 \(O(r-l)\) 种,每层中的复杂度是 \(O(n\log n)\),所以总的复杂度是 \(O(n\log ^2 n)\)。
实现上,我采用的是 unordered_map 去实现记录可能的转移点,用 map 略微卡常(可能是我写法比较大常吧),可能在做卷积的时候用二分队列会快一点。
说的可能比较简单,不太理解可以结合代码食用。
代码:
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
const int maxn = 3e5 + 5;
int n, a[maxn], t;
long long w[maxn], tot;
unordered_map<int, long long> f[maxn];
void renew(int pos, int p, long long v) {
if(!f[pos].count(p))
f[pos][p] = v;
else
f[pos][p] = min(f[pos][p], v);
}
vector<int> pos;
void solve(int lx, int rx, int l, int r, int p, vector<pair<int, long long> > &v) {
if(l > r)
return ;
// cout << lx << " " << rx << endl;
int mid = l + r >> 1, px = lx;
long long ans = 9e18;
for (int i = lx; i <= rx; i++) {
int id = pos[mid] - t - v[i].first;
if(id >= 0 && id <= tot) {
long long val = w[id] + v[i].second;
if(val < ans)
ans = val, px = i;
}
}
// if(pos[mid] == 5)
// cout << "Asdfsadf" << lx << " " << rx << " " << px << " " << ans << endl;
renew(p, pos[mid], ans);
solve(lx, px, l, mid - 1, p, v);
solve(px, rx, mid + 1, r, p, v);
}
void cal(int p, int lim, vector<pair<int, long long> > &v) {
int cur = -1;
sort(v.begin(), v.end());
pos.clear();
// cout << lim << "asdfadf" << endl;
for (int i = 0; i < v.size(); i++) {
int l = v[i].first + lim, r = l + tot;
for (int j = max(l, cur + 1); j <= r; j++) {
if(j > cur)
pos.push_back(j);
}
cur = r;
}
// cout << endl << v.size() << endl;
if(pos.size() && v.size())
solve(0, v.size() - 1, 0, pos.size() - 1, p, v);
}
void solve(int l, int r) {
// cout << l << "asdf" << r << endl;
// for (map<int, int>::iterator it = f[l].begin(); it != f[l].end(); it++)
// cout << it -> first << " " << it -> second << endl;
if(l + 1 == r) {
if(a[r] == -1) {
for (unordered_map<int, long long>::iterator it = f[l].begin(); it != f[l].end(); it++) {
renew(l + 1, it -> first, it -> second);
if(l - it -> first <= it -> first)
renew(l + 1, it -> first + 1, it -> second);
}
}
else {
for (unordered_map<int, long long>::iterator it = f[l].begin(); it != f[l].end(); it++) {
renew(l + 1, it -> first, it -> second);
renew(l + 1, it -> first + 1, it -> second + a[r]);
}
}
return ;
}
t = tot = 0;
for (int i = l + 1; i <= r; i++) {
if(a[i] == -1)
t++;
else
w[++tot] = a[i];
}
sort(w + 1, w + tot + 1);
for (int i = 1; i <= tot; i++)
w[i] += w[i - 1];
vector<pair<int, long long> > gw, gl, gm;
for (unordered_map<int, long long>::iterator it = f[l].begin(); it != f[l].end(); it++) {
if(it -> first >= r / 2 + 1)
gw.push_back(*it);
else if(it -> first <= (2 * l - r - 1) / 2)
gl.push_back(*it);
else
gm.push_back(*it);
}
int mid = l + r >> 1;
cal(r, t, gw);
t = 0, cal(r, 0, gl);
f[l].clear();
for (int i = 0; i < gm.size(); i++)
f[l].insert(gm[i]);
solve(l, mid), solve(mid, r);
}
long long ans[maxn];
void solve() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n + 1; i++)
f[i].clear();
f[0][0] = 0;
solve(0, n);
for (int i = 0; i <= n; i++)
ans[i] = f[n][i];
for (int i = n; i >= 1; i--)
ans[i - 1] = min(ans[i - 1], ans[i]);
for (int i = 0; i <= n; i++)
cout << ans[i] << " ";
cout << endl;
}
signed main() {
ios::sync_with_stdio(false);
int T; cin >> T;
while(T--)
solve();
return 0;
}
/*
1
5
100 -1 -1 -1 1
*/

浙公网安备 33010602011771号