CW高中-C0400D
Front
这么多人不会做 D 真是被想到,但不会做 B 的我属实是最难绷的。
时间管理的 dog 想摸鱼就写个题解混个时长。
如何笛卡尔树?不会。但是不至于。
Main
无论水池长得多奇怪,发现有用的只有横着的线段,称为平台。记录每个平台的深度和宽度,这一部分是非常简单的。
nl = read ();
for (int i = 1, x, y, lx = 0;i <= nl;++ i){
x = read ();
y = read ();
if (lx != x){
++ n;
h[n] = y;
w[n] = x - lx;
}
lx = x;
}
这样就把输入顺带处理完了。
出水口放在平台上一定不劣,且一个平台只需要放一个。
考虑一个平台的出水口可以吸收的最大水量,在其左边和右边各划一条线,只能想各自的方向或上方走,优先向各自的方向走,最后两条线与平台围成的面积就是水量大小。
我们要求的是 \(k\) 个平台水量面积并的最大值。
考虑启发式合并。维护几个平台的公共面积以及各个面积,公共面积可以延迟算。如果每次有合并就把公共面积给到拥有平台中面积最大的那个平台上,因为这部分公共面积如果被算就一定是包含了面积最大的那个平台。
深度从大到小合并,每次合并左中右三个平台(如果深度更大的话)。
然后从最后剩下的那一个集合里选前 \(k\) 大的就行了。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read (){
int p = 0;
char c = getchar ();
while (c < '0' || c > '9')
c = getchar ();
while (c >= '0' && c <= '9'){
p = (p << 1) + (p << 3) + (c - '0');
c = getchar ();
}
return p;
}
int nl, n;
int h[300005], w[300005], las[300005], s[300005];
int fa[300005], ord[300005];
multiset < int > pot[300005];
int sizei (int i){
return (int) pot[i].size ();
}
int find (int v){
if (fa[v] == v)
return v;
return fa[v] = find (fa[v]);
}
void Flush (int u, int y){
u = find (u);
s[u] += (las[u] - y) * w[u];
las[u] = y;
}
void push_down (int u){
int maxe = *pot[u].rbegin ();
pot[u].erase (pot[u].find (maxe));
pot[u].insert (maxe + s[u]);
s[u] = 0;
}
void Merge (int u, int v){
u = find (u);
v = find (v);
if (u != v){
if (sizei (u) < sizei (v))
swap (u, v);
push_down (u);
push_down (v);
for (int ele : pot[v])
pot[u].insert (ele);
w[u] += w[v];
fa[v] = u;
}
}
bool cmp1 (int i, int j){
return h[i] > h[j];
}
bool cmp2 (int a, int b){
return a > b;
}
signed main (){
nl = read ();
for (int i = 1, x, y, lx = 0;i <= nl;++ i){
x = read ();
y = read ();
if (lx != x){
++ n;
h[n] = y;
w[n] = x - lx;
}
lx = x;
}
for (int i = 1;i <= n;++ i){
las[i] = h[i];
ord[i] = fa[i] = i;
pot[i].insert (0);
}
sort (ord + 1, ord + n + 1, cmp1);
for (int i = 1;i <= n;++ i){
int u = ord[i];
if (u > 1 && h[u - 1] >= h[u]){
Flush (find (u - 1), h[u]);
Merge (u - 1, u);
}
if (u < n && h[u + 1] >= h[u]){
Flush (find (u + 1), h[u]);
Merge (u, u + 1);
}
}
int u = find (1);
Flush (u, 0);
int Ans = s[u], k;
k = read ();
while (! pot[u].empty () && k --){
Ans += *pot[u].rbegin ();
pot[u].erase (pot[u].find (*pot[u].rbegin ()));
}
printf ("%lld\n", Ans);
return 0;
}