CF573E Bear and Bowling
\mathcal O(n^2) 做法
设
显然有转移:
直接转移即可,时间复杂度
\mathcal O(n\sqrt{n}) 做法
有一个贪心的结论:
我们依次加入每个数,对于
引理 1. 在上述贪心策略下,如果
考虑归纳证明:假设引理 1 不成立,考虑整个过程中我们第一次违反这个引理的操作。如果
下面证明上面的贪心。
假设不成立,我们考虑第一个不满足这个贪心的时刻,对于某个大小
- 如果
B 中存在位置在x 的左边,考虑在x 的左边的最右位置y ,根据引理 1,此时有a_y \leq a_x ,V_x \geq V_y 。此时加入集合B 中的其他元素,我们考虑V_x,V_y 的变化,那么在x 右边的元素对V_x,V_y 的贡献一样,在y 左边的元素对V_x,V_y 的贡献是a_x,a_y ,而x,y 中间没有在B 中的元素,所以可以发现在其他元素加入之后V_x \geq V_y ,所以将B 中y 换成x 结果不劣。 - 如果
B 中只有在x 右边的元素,考虑在x 右边的最左位置y ,那么B 集合其他的元素对V_x,V_y 的贡献是一样的,所以把y 换成x 也不会更劣。
故上述假设不成立,贪心正确性证毕。
考虑分块维护凸包从而维护最大值。
每次选完最大值,就相当于对于选定的
时间复杂度
由于
\mathcal O(n \log n) 做法
有一个结论:对于
设
根据前面的贪心结论,若
首先可以去掉第一维,考虑二分出
所以用平衡树来实现即可。其实可以直接在平衡树上二分。时间复杂度
#include <bits/stdc++.h>
const int N = 1e6 + 10;
template < typename T >
inline void read(T &cnt)
{
cnt = 0; char ch = getchar(); bool op = 1;
for (; ! isdigit(ch); ch = getchar())
if (ch == '-') op = 0;
for (; isdigit(ch); ch = getchar())
cnt = cnt * 10 + ch - 48;
cnt = op ? cnt : - cnt;
}
int n, a[N];
long long D, P, ans;
namespace FHQ
{
// fhq treap 维护首项 a0 公差 d
// 以及当前值 val 和当前区间最右边的值 rval
struct treap
{
int lc, rc, siz; int rnd;
long long val, rval, a0, d;
} t[N];
int root, tot;
inline int New(long long x)
{
++ tot; t[tot].siz = 1;
t[tot].rnd = rand();
t[tot].val = t[tot].rval = x;
t[tot].a0 = t[tot].d = 0;
t[tot].lc = t[tot].rc = 0;
return tot;
}
inline void upd(int p)
{
t[p].siz = t[t[p].lc].siz + t[t[p].rc].siz + 1;
t[p].rval = t[p].rc ? t[t[p].rc].rval : t[p].val;
}
inline void addAll(int p, long long a0, long long d)
{
// a0 表示前一个 即 p 子树加的值分别为 a0 + d, a0 + 2d ...
t[p].val += a0 + (t[t[p].lc].siz + 1) * d;
t[p].rval += a0 + (t[p].siz) * d;
t[p].a0 += a0; t[p].d += d;
}
inline void psd(int p)
{
addAll(t[p].lc, t[p].a0, t[p].d);
addAll(t[p].rc, t[p].a0 + 1ll * (t[t[p].lc].siz + 1) * t[p].d, t[p].d);
t[p].a0 = t[p].d = 0;
}
inline void split(int p, int k, int &x, int &y)
{
if (! p)
{
x = y = 0;
return;
}
psd(p);
if (t[t[p].lc].siz >= k)
{
y = p;
split(t[p].lc, k, x, t[p].lc);
}
else
{
x = p;
split(t[p].rc, k - t[t[p].lc].siz - 1, t[p].rc, y);
}
upd(p);
}
inline int merge(int x, int y)
{
if (! x || ! y) return x + y;
if (t[x].rnd > t[y].rnd)
{
psd(x);
t[x].rc = merge(t[x].rc, y);
upd(x);
return x;
}
else
{
psd(y);
t[y].lc = merge(x, t[y].lc);
upd(y);
return y;
}
}
inline void search(int id, int p, int k, long long v)
{
if (! p) return;
psd(p);
int rk = k + t[t[p].lc].siz + 1;
if (t[p].val >= (t[p].lc ? t[t[p].lc].rval : v) + 1ll * rk * a[id])
{
P = rk, D = t[p].val;
search(id, t[p].rc, rk, t[p].val);
}
else search(id, t[p].lc, k, v);
}
inline void insert(int pos, long long val)
{
int x = 0;
split(root, pos, root, x);
root = merge(root, New(val));
root = merge(root, x);
}
inline void modify(int pos, long long a0, int d)
{
int x = 0;
split(root, pos, root, x);
addAll(x, a0, d);
root = merge(root, x);
}
inline void dfs(int p)
{
ans = std::max(ans, t[p].val);
psd(p);
if (t[p].lc) dfs(t[p].lc);
if (t[p].rc) dfs(t[p].rc);
}
}
int main()
{
read(n); srand(time(0));
for (int i = 1; i <= n; ++ i)
{
read(a[i]); D = P = 0; FHQ::search(i, FHQ::root, 0, 0);
FHQ::insert(P, D); FHQ::modify(P, P * a[i], a[i]);
// 插入一个数 以及 将一个区间加上一个等差数列
}
FHQ::dfs(FHQ::root);
std::cout << ans << std::endl;
return 0;
}