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;
}
本文作者:imcaigou
本文链接:https://www.cnblogs.com/imcaigou/p/17884373.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步