小 C 的屏幕保护程序 / COCI 2012~2013 CONTEST #4 Task AKVARIJ 题解

前言

原题面官方题解

题意简述

给你一个长为 n 的鱼缸的横截面。对其建立平面直角坐标系,左侧缸壁横坐标为 0,右侧缸壁横坐标为 n1。鱼缸底部障碍的起伏可以用 {hn} 描述,其中 hi 表示障碍在横坐标为 i 时高度为 hiii+1 间障碍的起伏可以看做以 (i,hi),(i+1,hi+1) 为两端的线段。

现在你要支持 m 次以下两种操作:

  1. 查询:给定水位高度 h0,求水淹没的面积(不包括障碍)。
  2. 修改:给定 i,v,将 hiv

n,m105,保证 0hi,h0,vV=103

题目分析

算法一:O((n+m)V)

发现 V 很小,考虑从这里入手。我们考虑维护 fH 表示 h0=H 时的答案。那么预处理时我们从小到大枚举 H,再依次枚举 i 统计 [i,i+1] 间部分对 fH 的贡献。首先让 fH 继承 fH1 的答案,然后考虑 H 增加一对答案的增量。事实上,我们可以不用继承答案,直接计算 fH,也是可以的。我们记 mi=min{hi,hi+1}mx=max{hi,hi+1}Δ=|hi+1hi|。那么我们分三类讨论:

  1. Hmi
    那么此时 [i,i+1] 一点水都没有,没有任何贡献。
  2. H(mi,mx]
    增量为一个梯形,根据相似三角形,上底 1×HmiΔ,下底 1×H1miΔ,高 1,于是这部分面积为 S=Hmi12Δ,令 fHfH+S
  3. H>mx
    增量即为一个 1×1 的正方形,fHfH+1

于是,我们可以在 O(nV) 的时间内预处理出 f。询问即可 O(1)。考虑修改,发现修改仅需要将原先 [i,i+1] 的贡献在 f 中删除,然后再将修改后的答案加进去即可,单次修改 O(V)。总时间复杂度为 O((n+m)V),可以通过。

算法二:O((n+m)logV)

算法一菜飞了,但凡 V 再大一点就吃不消了,考虑优化。

我们很自然地发现,[i,i+1] 对答案的贡献按照上面的分讨,分为三个部分,我们不考虑增量,而是考虑总贡献:

  1. 对于 Hmi
    没有任何贡献。
  2. 对于 H(mi,mx]
    贡献为一个三角形,竖直边长 Hmi,横边长 1×HmiΔ,贡献为 (Hmi)22Δ=12ΔH2miΔH+mi22Δ
  3. 对于 H>mx
    贡献贡献为一个梯形,拆分成一个三角形和一个矩形。S=Δ2,矩形面积为 Hmx,总贡献为 H+(Δ2mx)

读者已经猜到我想要表示什么了,这三种情况可以归约到关于 H 的二次函数,我们只需要分别维护对应系数之和即可。至于 H 的限制,使用树状数组即可。

时间复杂度:O((n+m)logV),瓶颈在于树状数组。

代码

算法一:O((n+m)V)
#include <cstdio>
using namespace std;
#define isdigit(x) ('0' <= x && x <= '9')
inline void read(int &x) {
x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar());
for (; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
}
inline char read() {
char ch; do ch = getchar(); while (ch != 'Q' && ch != 'U');
return ch;
}
inline int abs(int x) { return x < 0 ? -x : x; }
inline int min(int a, int b) { return a < b ? a : b; }
inline int max(int a, int b) { return a > b ? a : b; }
const int N = 100010, V = 1010;
using ld = long double;
const ld eps = 1e-10;
int n, m, h[N], mii[N], derr[N];
ld ans[V];
signed main() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(h[i]), mii[i - 1] = min(h[i], h[i - 1]), derr[i - 1] = abs(h[i] - h[i - 1]);
for (int H = 1; H <= 1000; ++H) {
ans[H] = ans[H - 1];
for (int i = 1, mi, der; i + 1 <= n; ++i) {
mi = mii[i];
if (H <= mi) {
} else if (H <= max(h[i], h[i + 1])) {
der = derr[i];
ans[H] += (H - mi - 0.5) / der;
} else {
ans[H] += 1;
}
}
}
for (int H, p, v; m--; ) {
char op = read();
if (op == 'Q') {
read(H);
#ifdef XuYueming
printf(">>> ");
#endif
printf("%.1Lf\n", ans[H] + eps);
} else {
read(p), read(v), ++p;
ld tot = 0;
int mi = mii[p], der = derr[p];
int MI = mii[p - 1], DER = derr[p - 1];
for (int H = 1; H <= 1000; ++H) {
if (p + 1 <= n) {
if (H <= mi) {
} else if (H <= max(h[p], h[p + 1])) {
tot += (H - mi - 0.5) / der;
} else {
tot += 1;
}
}
if (p - 1 >= 1) {
if (H <= MI) {
} else if (H <= max(h[p], h[p - 1])) {
tot += (H - MI - 0.5) / DER;
} else {
tot += 1;
}
}
ans[H] -= tot;
}
h[p] = v;
tot = 0;
mi = mii[p] = min(h[p], h[p + 1]);
der = derr[p] = abs(h[p] - h[p + 1]);
MI = mii[p - 1] = min(h[p - 1], h[p]);
DER = derr[p - 1] = abs(h[p - 1] - h[p]);
for (int H = 1; H <= 1000; ++H) {
if (p + 1 <= n) {
if (H <= mi) {
} else if (H <= max(h[p], h[p + 1])) {
tot += (H - mi - 0.5) / der;
} else {
tot += 1;
}
}
if (p - 1 >= 1) {
if (H <= MI) {
} else if (H <= max(h[p], h[p - 1])) {
tot += (H - MI - 0.5) / DER;
} else {
tot += 1;
}
}
ans[H] += tot;
}
}
}
return 0;
}
算法二:O((n+m)logV)
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 100010;
const int V = 1005;
using ld = long double;
int n, m, h[N];
struct Bit_Tree {
ld tree[V];
inline void modify(int p, ld v) {
for (; p < V; p += p & -p)
tree[p] += v;
}
inline void modify(int l, int r, ld v) {
modify(l, v), modify(r + 1, -v);
}
inline ld query(int p) {
ld res = 0;
for (; p; p &= p - 1)
res += tree[p];
return res;
}
} t[3];
inline void add(int i, int f) {
int mi = min(h[i], h[i + 1]);
int mx = max(h[i], h[i + 1]);
int delta = mx - mi;
if (mi + 1 <= mx) {
t[2].modify(mi + 1, mx, f * (.5 / delta));
t[1].modify(mi + 1, mx, f * (-1. * mi / delta));
t[0].modify(mi + 1, mx, f * (.5 * mi * mi / delta));
}
t[1].modify(mx + 1, f * 1);
t[0].modify(mx + 1, f * (delta / 2. - mx));
}
inline ld query(int h) {
return t[2].query(h) * h * h + t[1].query(h) * h + t[0].query(h);
}
signed main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", &h[i]);
for (int i = 1; i < n; ++i)
add(i, 1);
for (int h0, p, v; m--; ) {
char op[2];
scanf("%s", op);
if (*op == 'Q') {
scanf("%d", &h0);
printf("%.1Lf\n", query(h0));
} else {
scanf("%d%d", &p, &v), ++p;
if (p > 1) add(p - 1, -1);
if (p < n) add(p, -1);
h[p] = v;
if (p > 1) add(p - 1, 1);
if (p < n) add(p, 1);
}
}
return 0;
}
卡常后代码
#include <cstdio>
const int MAX = 1 << 26;
char buf[MAX], *inp = buf;
template <typename T>
inline void read(T &x) {
x = 0; char ch = *inp++;
for (; ch < 48; ch = *inp++);
for (; ch >= 48; ch = *inp++) x = (x << 3) + (x << 1) + (ch ^ 48);
}
const int N = 100010;
const int V = 1005;
using ld = long double;
int n, m, h[N];
ld tree[V][3];
inline void add(int i, bool del) {
int mi = h[i] < h[i + 1] ? h[i] : h[i + 1];
int mx = h[i] ^ h[i + 1] ^ mi;
int delta = mx - mi;
if (mi < mx) {
int l = mi + 1, r = mx + 1;
ld v2 = .5 / delta, v1 = -1. * mi / delta, v0 = mi * mi * v2;
if (del) v0 = -v0, v1 = -v1, v2 = -v2;
for (; l < r; l += l & -l) {
tree[l][0] += v0;
tree[l][1] += v1;
tree[l][2] += v2;
}
for (; r < l && r < V; r += r & -r) {
tree[r][0] -= v0;
tree[r][1] -= v1;
tree[r][2] -= v2;
}
for (; l < r; l += l & -l) {
tree[l][0] += v0;
tree[l][1] += v1;
tree[l][2] += v2;
}
}
ld v1 = 1, v0 = delta / 2. - mx;
if (del) v1 = -v1, v0 = -v0;
for (int p = mx + 1; p < V; p += p & -p) {
tree[p][0] += v0;
tree[p][1] += v1;
}
}
inline ld query(int h) {
ld v2 = 0, v1 = 0, v0 = 0;
for (int p = h; p; p &= p - 1) {
v0 += tree[p][0];
v1 += tree[p][1];
v2 += tree[p][2];
}
return v2 * h * h + v1 * h + v0;
}
signed main() {
fread(buf, 1, MAX, stdin), read(n), read(m), read(h[1]);
for (int i = 2; i <= n; ++i)
read(h[i]), add(i - 1, false);
for (int h0, p, v; m--; ) {
char op;
do op = *inp++; while (op != 'Q' && op != 'U');
if (op == 'Q') {
read(h0);
printf("%.1Lf\n", query(h0));
} else {
read(p), read(v), ++p;
if (p > 1) add(p - 1, true);
if (p < n) add(p, true);
h[p] = v;
if (p > 1) add(p - 1, false);
if (p < n) add(p, false);
}
}
return 0;
}
posted @   XuYueming  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示