题意:
一个有热带鱼的水族馆,水族馆的底端是由沙石形成的供鱼玩耍的地方,沙石的高度可以设置,水位也可以设置。水族馆可以看做是一个二维平面,宽看作N-1 列,最左端的横坐标为0,最右端横坐标为N-1,每个整数横坐标都对应着一个沙石的高度H_i(0<=i<=N-1),相邻横坐标i 和i+1 之间的沙石可以看做是由(i,H_i)和(i+1,H_i+1)这两个点形成的线段。如果水位为h,水覆盖着水族馆底端到y=h 这个区域,如果有部分沙石在水面以上,这部分形成一个岛屿。对于不同的沙石情况,你想知道被水覆盖区域的面积,即水位以下总面积减去水中沙石的面积。
N(3<=N<=100,000)
询问数M(1<=M<=100,000)
沙石高度H_i(0<=Hi<=1000)
分析:
设ans[h] 为 水面高度为h时水面下沙石的面积。
考虑两个相邻点组成的图形的面积对ans[h]的贡献,设它们的高度分别是X和Y。
1°
h≤min(x,y)
时,这个图形对ans[h]的贡献是一个一次函数。
2° min(x,y)<
h≤max(x,y)
时,这个图形对ans[h]的贡献是一个二次函数。
3°max(x,y)
≤
h时,这个图形对ans[h]的贡献是一个常数。
所以我们可以在ans[h]记录答案关于h的二次项系数、一次项系数、常数。用线段树上述三种情况进行区间修改。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef double Do;
const int N = 1e5 + 10;
int h[N];
double pass[N / 100 * 4][3],ans[N][3];
int n,m;
void push(int tot,int l,int r) {
if (!pass[tot][0] && !pass[tot][1] && !pass[tot][2]) return;
if (l == r) {
for (int i = 0;i < 3;i ++) ans[l][i] += pass[tot][i];
}
else {
for (int i = 0;i < 3;i ++) {
pass[tot << 1][i] += pass[tot][i];
pass[tot << 1 | 1][i] += pass[tot][i];
}
}
for (int i = 0;i < 3;i ++) pass[tot][i] = 0;
}
void ins(int tot,int l,int r,int l1,int r1,Do a,Do b,Do c) {
if (l1 > r1) return;
if (l1 <= l && r1 >= r) {
pass[tot][2] += a;
pass[tot][1] += b;
pass[tot][0] += c;
push(tot,l,r);
return;
}
push(tot,l,r);
int mid = (l + r) >> 1;
if (l1 <= mid) ins(tot << 1,l,mid,l1,r1,a,b,c);
if (mid < r1) ins(tot << 1 | 1,mid + 1,r,l1,r1,a,b,c);
}
void add(int X,int Y,int t) {
double x = X,y = Y;
if (x > y) swap(x,y);
ins(1,1,1000,1,min(X,Y),0,t,0);
double z = y - x;
ins(1,1,1000,min(X,Y) + 1,max(X,Y),-t * 1 / (2 * z),t * y / z,t * (x + z / 2 - y * y / (2 * z)));
ins(1,1,1000,max(X,Y) + 1,1000,0,0,t * (x + y) / 2);
}
void get(int tot,int l,int r,int pos) {
push(tot,l,r);
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) get(tot << 1,l,mid,pos);
else get(tot << 1 | 1,mid + 1,r,pos);
}
int 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(h[i],h[i + 1],1);
for (int i = 1;i <= m;i ++) {
char type;
scanf(" %c",&type);
if (type == 'Q') {
int H;
scanf("%d",&H);
Do Ans = (n - 1) * H;
H = min(H,1000);
get(1,1,1000,H);
Do cur = 0,x = 1;
for (int j = 0;j <= 2;j ++) {
cur += x * ans[H][j];
x *= H;
}
printf("%.3lf\n",Ans - cur);
}
else {
int k,H;
scanf("%d%d",&k,&H);
k ++;
if (k < n) add(h[k],h[k + 1],-1),add(H,h[k + 1],1);
if (k > 1) add(h[k - 1],h[k],-1),add(h[k - 1],H,1);
h[k] = H;
}
}
}