欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF573E Bear and Bowling 题解

Description

  • 给定一个长度为 n 的序列 a1n
  • 你要求一个 a 的子序列 b1m(可以为空),使得 i=1mibi 的值最大。
  • n105|ai|107

Solution

有一个显然的 dp 是设 fi,j 表示前 i 个数,选 j 个数的最大值,转移即为:fi,j=max{fi1,j,fi1,j1+jai},由于这题时限很大并且 n 很小,所以这个能过。。。

考虑优化。

有一个结论是对于每个 i,都存在一个分界点 ki,使得对于 j<kifi,j=fi1,j,对于 jkifi,j=fi1,j1+jai

证明就考虑设 gi,j=fi,jfi,j1,那么 fi,j=fi1,j 的充分必要条件为 fi1,jfi1,j1+jai,即 gi1,jjai

通过观察可以发现 gi,jj 对于 j 单调不增,那么不妨假设 gi1,jj 单调不增,考虑归纳证明 gi 也满足条件。

k 为满足 gi1,jj<ai 的最小的 j,则对于 j[0,k1]gi,j=gi1,j。同时 gi,k 变为 kai,所以 gi,kk=aigi,k1k1

对于 j>kgi,j=gi1,j1+ai,所以对于 j>k 满足 gi,jjgi,j+1j+1 的条件为:

gi1,j1+aijgi1,j+aij+1gi1,j1jj+1gi1,j1j+1aigi1,j1(jj+1gi1,j1j+1ai)0

又因为:

LHSj1jgi1,jjj+1gi1,j+1j+1ai=jaigi1,jj(j+1)0

所以结论得证。

然后就可以从前往后枚举 ai,维护 g 数组,每次相当于是在某个位置插入和将某个后缀区间加某个数,并且需要快速找到分界点,可以用平衡树维护。

时间复杂度:O(nlogn)

Code

#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 1e5 + 5;
int n;
int64_t a[kMaxN];
struct FHQTreap {
int tot = 0, rt, ch[kMaxN][2], rd[kMaxN], sz[kMaxN];
int64_t val[kMaxN], rnk[kMaxN], tag1[kMaxN], tag2[kMaxN];
std::mt19937 rnd;
int newnode(int64_t x, int64_t y) {
val[++tot] = x, rnk[tot] = y;
ch[tot][0] = ch[tot][1] = tag1[tot] = tag2[tot] = 0, rd[tot] = rnd();
sz[tot] = 1;
return tot;
}
FHQTreap() {
tot = 0, rnd.seed(std::random_device{}());
rt = newnode(-1e18, 1);
}
void pushup(int x) {
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
}
void addtag(int x, int64_t v1, int64_t v2) {
val[x] += v1, rnk[x] += v2, tag1[x] += v1, tag2[x] += v2;
}
void pushdown(int x) {
if (ch[x][0]) addtag(ch[x][0], tag1[x], tag2[x]);
if (ch[x][1]) addtag(ch[x][1], tag1[x], tag2[x]);
tag1[x] = tag2[x] = 0;
}
int merge(int x, int y) {
if (!x || !y) return x + y;
pushdown(x), pushdown(y);
if (rd[x] < rd[y]) {
ch[x][1] = merge(ch[x][1], y), pushup(x);
return x;
} else {
ch[y][0] = merge(x, ch[y][0]), pushup(y);
return y;
}
}
void split(int x, int v, int &a, int &b) { // a : >= v, b : < v
if (!x) return void(a = b = 0);
pushdown(x);
if (val[x] >= rnk[x] * v) {
a = x, split(ch[x][1], v, ch[a][1], b);
} else {
b = x, split(ch[x][0], v, a, ch[b][0]);
}
pushup(x);
}
void ins(int64_t x) {
int a, b;
split(rt, x, a, b);
if (b) addtag(b, x, 1);
rt = merge(merge(a, newnode(x * (sz[a] + 1), sz[a] + 1)), b);
}
int64_t getval(int x) {
if (!x) return 0;
pushdown(x);
int64_t ret = std::max<int64_t>(val[x], 0);
return ret + getval(ch[x][0]) + getval(ch[x][1]);
}
} t;
void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
t.ins(a[i]);
}
std::cout << t.getval(t.rt) << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
posted @   下蛋爷  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起