首先说明一下本人线段树风格:
用结构体数组t来表示线段树,左子树为l,右子树为r,懒惰标记为lazy。
在更新和查询函数中,为了防止混淆,只传递更新/查询的区间l和r,当前树的编号p,以及更新的值x。树的左右结点就用t[p].l和t[p].r表示(为了防止混淆)。
另外两个函数分别记为push_up和push_down。
在区间修改的时候,这一段代码是比较容易混淆的,如果搞不清为什么的话就直接背下来吧~
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) res += ask(p<<1,l,r);
if(r>mid) res += ask(p<<1|1,l,r);
打*的是比较好的题目
文章目录
HDU1166 单点修改区间求和
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1166
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,data;
} t[maxn*4];
ll a[maxn],T,n;
string s;
void build(ll p,ll l,ll r) {
t[p].data = 0;
t[p].l = l, t[p].r = r;
if(l==r) {t[p].data=a[l]; return;}
ll mid = (l+r) / 2;
build(p<<1, l, mid);
build(p<<1|1,mid+1,r);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
void update(ll p,ll x,ll v) {
if(t[p].l==t[p].r) {t[p].data += v; return;}
ll mid = (t[p].l + t[p].r) / 2;
if(x<=mid) update(p<<1,x,v);
else update(p<<1|1,x,v);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
ll ask(ll p,ll l,ll r) {
ll res = 0;
if(l<=t[p].l && r>=t[p].r) return t[p].data;
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) res += ask(p<<1,l,r);
if(r>mid) res += ask(p<<1|1,l,r);
return res;
}
int main() {
read(T);
for(int i=1;i<=T;i++) {
printf("Case %d:\n", i);
read(n);
for(int i=1;i<=n;i++) read(a[i]);
build(1,1,n);
while(cin>>s) {
ll i,j;
if(s=="End") break;
read(i), read(j);
if(s=="Add") update(1,i,j);
else if(s=="Sub") update(1,i,-j);
else printf("%lld\n",ask(1,i,j));
}
}
return 0;
}
HDU1754 单点修改区间最值
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1754
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,data;
} t[maxn*4];
ll n,m,a[maxn];
string s;
void build(ll p,ll l,ll r) {
t[p].data = 0;
t[p].l = l, t[p].r = r;
if(l==r) {t[p].data=a[l]; return;}
ll mid = (l+r) / 2;
build(p<<1, l, mid);
build(p<<1|1,mid+1,r);
t[p].data = max(t[p<<1].data , t[p<<1|1].data);
}
void update(ll p,ll x,ll v) {
if(t[p].l==t[p].r) {t[p].data = v; return;}
ll mid = (t[p].l + t[p].r) / 2;
if(x<=mid) update(p<<1,x,v);
else update(p<<1|1,x,v);
t[p].data = max(t[p<<1].data , t[p<<1|1].data);
}
ll ask(ll p,ll l,ll r) {
ll res = -1e9;
if(l<=t[p].l && r>=t[p].r) return t[p].data;
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) res = max(res,ask(p<<1,l,r));
if(r>mid) res = max(res,ask(p<<1|1,l,r));
return res;
}
int main() {
while(~scanf("%lld%lld",&n,&m)) {
for(int i=1;i<=n;i++) read(a[i]);
build(1,1,n);
for(ll i=0,l,r;i<m;i++) {
cin >> s;
read(l), read(r);
if(s=="U") update(1,l,r);
else printf("%lld\n",ask(1,l,r));
}
}
return 0;
}
HDU1394 求循环数组逆序对
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题解
求逆序对很简单,就是遍历一遍i,求1~a[i]-1中间有多少数,然后再在a[i]上面+1就行了。
本题数组会出现循环,但是找规律可以发现,由于数组是一个n的全排列,所以当开头数a[1]放到末尾时,逆序对个数必然会减少a[1]个,然后增加n-(a[1]+1)个。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,data;
} t[maxn*4];
ll a[maxn],T,n;
string s;
void build(ll p,ll l,ll r) {
t[p].data = 0;
t[p].l = l, t[p].r = r;
if(l==r) {t[p].data=0; return;}
ll mid = (l+r) / 2;
build(p<<1, l, mid);
build(p<<1|1,mid+1,r);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
void update(ll p,ll x,ll v) {
if(t[p].l==t[p].r) {t[p].data += v; return;}
ll mid = (t[p].l + t[p].r) / 2;
if(x<=mid) update(p<<1,x,v);
else update(p<<1|1,x,v);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
ll ask(ll p,ll l,ll r) {
ll res = 0;
if(l<=t[p].l && r>=t[p].r) return t[p].data;
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) res += ask(p<<1,l,r);
if(r>mid) res += ask(p<<1|1,l,r);
return res;
}
int main() {
while(~scanf("%lld", &n)) {
ll ans = 0;
build(1,1,n);
for(int i=1;i<=n;i++) read(a[i]),a[i]++;
for(int i=n;i;i--) {
ans += ask(1,1,a[i]-1);
update(1,a[i],1);
}
ll res = ans;
for(int i=1;i<=n;i++) {
a[i]--;
ans = ans - a[i] + n - (a[i] + 1);
res = min(res,ans);
}
printf("%lld\n", res);
}
return 0;
}
*HDU2795 区间求最大值的位置
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=2795
题目
给定一个 h ∗ w h*w h∗w的白板,要求往上面贴n张1*w[i]的海报,要求贴的海报尽量靠上,在最靠上的前提下尽量靠左。要求贴一张海报输出一下贴的的行的位置(从上向下从1开始编号)。
题解
线段树维护每一行的剩余长度,然后贴一张海报之前查询一下能贴在哪个位置,由于一定是能贴在具体的一行,所以是会细化到具体的叶子结点。
由于h的范围是1e9,但是可以看出最多只贴n张海报,而n的范围是1e6的,所以建树的时候右边界为min(h,n)即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
typedef long long ll;
struct SegementTree {
ll l,r,data;
}t[maxn*4];
ll h,w,n;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
void build(ll p,ll l,ll r) {
t[p].l = l, t[p].r = r, t[p].data=0;
if(l==r) {t[p].data=w; return;}
ll mid = (l + r) / 2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
t[p].data = max(t[p<<1].data,t[p<<1|1].data);
}
ll ask(ll p,ll x) {
ll res = 0, l = t[p].l, r = t[p].r;
if(l==r) {
t[p].data -= x;
return l;
}
if(x<=t[p<<1].data) res = ask(p<<1,x);
else if(x<=t[p<<1|1].data) res = ask(p<<1|1,x);
t[p].data = max(t[p<<1].data,t[p<<1|1].data);
return res;
}
int main() {
while(~scanf("%lld%lld%lld",&h,&w,&n)) {
ll temp = min(n,h);
build(1,1,temp);
while(n--) {
ll x;
read(x);
if(t[1].data>=x) {
printf("%lld\n", ask(1,x));
} else puts("-1");
}
}
return 0;
}
HDU1698 区间替换区间求和
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1698
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,data,lazy;
} t[maxn*4];
ll a[maxn],T,n,q,maxi;
string s;
void build(ll p,ll l,ll r) {
t[p].data = t[p].lazy = 0;
t[p].l = l, t[p].r = r;
if(l==r) {t[p].data=1; return;}
ll mid = (l+r) / 2;
build(p<<1, l, mid);
build(p<<1|1,mid+1,r);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
void push_down(ll p) {
if(t[p].lazy) {
t[p<<1].data = t[p].lazy * (t[p<<1].r - t[p<<1].l + 1);
t[p<<1|1].data = t[p].lazy * (t[p<<1|1].r - t[p<<1|1].l + 1);
t[p<<1].lazy = t[p<<1|1].lazy = t[p].lazy;
t[p].lazy = 0;
}
}
void update(ll p,ll l,ll r,ll x) {
if(l<=t[p].l && r>=t[p].r) {
t[p].data = x*(t[p].r-t[p].l+1);
t[p].lazy = x;
return;
}
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) update(p<<1,l,r,x);
if(r>mid) update(p<<1|1,l,r,x);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
int main() {
read(T);
for(int cnt=1;cnt<=T;cnt++) {
read(n);
build(1,1,n);
read(q);
while(q--) {
ll x,y,z;
read(x), read(y), read(z);
update(1,x,y,z);
}
// for(int i=1;i<=30;i++) cout << t[i].data << endl;
printf("Case %lld: The total value of the hook is %lld.\n",cnt,t[1].data);
}
return 0;
}
POJ3468 区间增减区间求和
题目链接
https://cn.vjudge.net/contest/296742#problem/F
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int maxn = 200005;
typedef long long ll;
struct SegementTree {
ll l,r,data,lazy;
}t[maxn*4];
ll h,w,n,a[maxn],q;
string s;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
void push_down(ll p) {
if(t[p].lazy) {
t[p<<1].data += t[p].lazy * (t[p<<1].r - t[p<<1].l + 1);
t[p<<1|1].data += t[p].lazy * (t[p<<1|1].r - t[p<<1|1].l + 1);
t[p<<1].lazy += t[p].lazy;
t[p<<1|1].lazy += t[p].lazy;
t[p].lazy = 0;
}
}
void build(ll p,ll l,ll r) {
t[p].l = l, t[p].r = r, t[p].data = t[p].lazy = 0;
if(l==r) {t[p].data=a[l];return;}
ll mid = (l+r) / 2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
void update(ll p,ll l,ll r,ll x) {
if(l<=t[p].l && r>=t[p].r) {
t[p].data += x * (t[p].r - t[p].l + 1);
t[p].lazy += x;
return;
}
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) update(p<<1,l,r,x);
if(r>mid) update(p<<1|1,l,r,x);
t[p].data = t[p<<1].data + t[p<<1|1].data;
}
ll ask(ll p,ll l,ll r) {
ll res = 0;
if(l<=t[p].l && r>=t[p].r) return t[p].data;
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) res += ask(p<<1,l,r);
if(r>mid) res += ask(p<<1|1,l,r);
return res;
}
int main() {
read(n), read(q);
for(int i=1;i<=n;i++) read(a[i]);
build(1,1,n);
while(q--) {
cin >> s;
if(s=="C") {
ll x,y,z;
read(x), read(y), read(z);
update(1,x,y,z);
} else {
ll x,y;
read(x), read(y);
printf("%lld\n",ask(1,x,y));
}
}
return 0;
}
*POJ2528 离散化+成段替换+区间查询
题目链接
http://poj.org/problem?id=2528
题目
给定一个长度为n的白板,往上面贴海报。
每次给出海报所覆盖的区间[L,R],要求出最后露在外面的海报的张数。
题解
由于本题数据过大,需要离散化。
离散化后将每张海报编号,查询的时候由于只查找一次,所以就细化到每个叶子结点,统计有多少个不同的编号。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 100005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,data,lazy;
}t[maxn*4];
ll n,x,y,cnt,m,a[maxn],T,b[maxn],ans;
bool vis[20005];
vector<pair<ll,ll> >e;
void build(ll p,ll l,ll r) {
t[p].l = l, t[p].r = r, t[p].data = t[p].lazy = 0;
if(l==r) {t[p].lazy=-1;return;}
ll mid = (l+r) / 2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void push_down(ll p) {
if(t[p].lazy) {
t[p<<1].data = t[p<<1|1].data = t[p].data;
t[p<<1].lazy = t[p<<1|1].lazy = t[p].lazy;
t[p].lazy = 0;
}
}
void update(ll p,ll l,ll r,ll x) {
// cout << p<<' ' <<t[p].l<<' ' << t[p].r << ' '<< l << ' ' << r << endl;
if(l<=t[p].l && r>=t[p].r) {t[p].data = t[p].lazy =x;return;}
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) update(p<<1,l,r,x);
if(r>mid) update(p<<1|1,l,r,x);
}
void ask(ll p,ll l,ll r) {
if(t[p].l==t[p].r) {
if(!vis[t[p].data]) {
vis[t[p].data] = 1;
ans++;
}
return ;
}
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(l<=mid) ask(p<<1,l,r);
if(r>mid) ask(p<<1|1,l,r);
}
void discrete() {
sort(a,a+cnt);
b[m++] = a[0];
for(int i=1;i<cnt;i++) {
if(a[i]!=a[i-1]) b[m++]=a[i];
}
}
ll query(ll x) {
return lower_bound(b,b+m,x)-b;
}
int main() {
read(T);
while(T--) {
memset(vis,0,sizeof(vis));
cnt = m = ans = 0;
e.clear();
read(n);
for(int i=0;i<n;i++) {
read(x), read(y);
a[cnt++] = x;
a[cnt++] = y;
e.push_back(make_pair(x,y));
}
discrete();
build(1,1,m);
for(int i=0;i<n;i++) {
x = e[i].first, y = e[i].second;
x = query(x)+1, y = query(y)+1;
// cout << x << ' ' << y << endl;
update(1,x,y,i+1);
}
ask(1,1,m);
cout << ans << endl;
}
return 0;
}
*POJ3367 区间合并
题目链接
http://poj.org/problem?id=3667
题目
给定一个长长的旅馆,里面有n间连续的屋子。
再给出m个询问,每次要么要求住进来a[i]个人(他们必须住连续的房间)
或者让[L,R]区间的人离开(允许屋子本来就是空的)
题解
线段树维护每个屋子是否住了人,标记为0/1。住人进来的时候,先查询从哪个结点开始连续的一段没有人,然后在更新这一段。
而查询的过程就略显麻烦,需要维护三个值,lsum表示从最左边开始连续的空房间数量,rsum表示从右边开始连续的空房间数量,sum表示整段中连续的空房间数量。这三个关系的维护(即push_up函数)是本体的一个难点。
代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 50005;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x *= f;
}
struct SegementTree {
ll l,r,lsum,rsum,sum,lazy;
}t[maxn*4];
ll n,m;
void build(ll p,ll l,ll r) {
t[p].l = l, t[p].r = r;
t[p].lsum = t[p].rsum = t[p].sum = r-l+1;
t[p].lazy = -1; //懒惰标记初始为-1
if(l==r) {t[p].sum = 1;return;}
ll mid = (l+r) / 2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void push_down(ll p) {
if(t[p].lazy!=-1) { //0的话表示走人,所以这一段的空房间数量=区间长度
t[p<<1].lsum = t[p<<1].rsum = t[p<<1].sum = t[p].lazy ? 0 : t[p<<1].r-t[p<<1].l+1;
t[p<<1|1].lsum = t[p<<1|1].rsum = t[p<<1|1].sum = t[p].lazy ? 0 : t[p<<1|1].r-t[p<<1|1].l+1;
t[p<<1].lazy = t[p<<1|1].lazy = t[p].lazy;
t[p].lazy = -1;
}
}
void push_up(ll p) {
t[p].lsum = t[p<<1].lsum;
t[p].rsum = t[p<<1|1].rsum;
ll k = t[p].r - t[p].l + 1;
if(t[p<<1].lsum==t[p<<1].r-t[p<<1].l+1) t[p].lsum += t[p<<1|1].lsum;
if(t[p<<1|1].rsum==t[p<<1|1].r-t[p<<1|1].l+1) t[p].rsum += t[p<<1].rsum;
t[p].sum = max(t[p<<1].rsum+t[p<<1|1].lsum,max(t[p<<1].sum,t[p<<1|1].sum));
}
void update(ll p,ll l,ll r,ll x) {
if(l<=t[p].l && r>=t[p].r) {
if(x)
t[p].lsum = t[p].rsum = t[p].sum = 0;
else
t[p].lsum = t[p].rsum = t[p].sum = t[p].r - t[p].l + 1;
t[p].lazy = x;
return;
}
push_down(p);
ll mid = (t[p].l+t[p].r) / 2;
if(l<=mid) update(p<<1,l,r,x);
if(r>mid) update(p<<1|1,l,r,x);
push_up(p);
}
ll ask(ll p,ll x) {
if(t[p].l==t[p].r) return 1;
push_down(p);
ll mid = (t[p].l + t[p].r) / 2;
if(t[p<<1].sum >= x) return ask(p<<1,x);
else if(t[p<<1].rsum + t[p<<1|1].lsum >= x) return mid-t[p<<1].rsum+1;
else return ask(p<<1|1,x);
}
int main() {
read(n), read(m);
build(1,1,n);
for(ll i=0,f,x,y;i<m;i++) {
read(f);
if(f==1) {
read(x);
if(t[1].sum < x) {puts("0");continue;}
ll pos = ask(1,x);
printf("%lld\n",pos);
update(1,pos,pos+x-1,1);
} else {
read(x), read(y);
update(1,x,x+y-1,0);
}
}
return 0;
}
*HDU1542 扫描线求矩形面积并
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1542
题目
给定n个矩形,要求它们面积的并集。
题解
用扫描线来做。
关于扫描线,我觉得这一篇博客讲的不错https://blog.csdn.net/xianpingping/article/details/83032798?tdsourcetag=s_pcqq_aiomsg
所谓扫描线,就是对于一个矩形,只维护竖着的两条边,这样横着的两条边就可以看作两条扫描线,从下向上扫描,在遇到矩形的下底边时,给这个区间的cnt+1,表示当前有至少一条边,当遇到上底边的时候就-1,减到0的时候该边就不存在了。每次用 两个扫描线的距离* 当前所有扫描线投影到x轴的长度 表示矩形的面积。
本题由于坐标都是浮点数,所以对于x坐标先离散化一下(by数组b),然后用线段树维护,线段树的每个叶子结点x维护的是从[b[x],b[x+1]]的这个区间的长度,所以更新的区间离散化后记为L和R,那么R应该-1(因为维护的最右边刚刚够到R点的位置,却不能加上它维护的那个区间),而在统计的时候累加结果应该用b[r+1]-b[l],因为b[r+1]所在的位置正好是b[r]维护区间的右端点。
代码
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <vector>
using namespace std;
const int maxn = 200005;
struct node {
double l, r, h;
int d;
node() {}
node(double l, double r, double h, int d) : l(l), r(r), h(h), d(d) {}
bool operator<(const node &b) const { return h < b.h; }
};
struct SegementTree {
int l, r, cnt;
double sum;
} t[maxn * 4];
vector<node> e;
vector<double> s;
double b[maxn];
int n, m, num;
void discrete() {
sort(e.begin(), e.end());
sort(s.begin(), s.end());
b[++m] = s[0];
for (int i = 1; i < s.size(); i++) {
if (s[i] != s[i - 1]) b[++m] = s[i];
}
}
int query(double x) { return lower_bound(b + 1, b + m + 1, x) - b; }
void push_up(int p) {
if (t[p].cnt) {
t[p].sum = b[t[p].r + 1] - b[t[p].l];
} else if (t[p].l == t[p].r)
t[p].sum = 0;
else
t[p].sum = t[p << 1].sum + t[p << 1 | 1].sum;
}
void build(int p, int l, int r) {
t[p].l = l, t[p].r = r;
t[p].sum = t[p].cnt = 0;
if (l == r) {
return;
}
int mid = (l + r) / 2;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
push_up(p);
}
void update(int p, int l, int r, int x) {
if (l <= t[p].l && r >= t[p].r) {
t[p].cnt += x;
push_up(p);
return;
}
int mid = (t[p].l + t[p].r) / 2;
if (l <= mid) update(p << 1, l, r, x);
if (r > mid) update(p << 1 | 1, l, r, x);
push_up(p);
}
int main() {
while (cin>>n) {
if(!n) break;
double a1, b1, a2, b2, ans = 0;
m = 0;
e.clear();
s.clear();
for (int i = 0; i < n; i++) {
cin >> a1 >> b1 >> a2 >> b2;
e.push_back(node(a1, a2, b1, 1));
s.push_back(a1);
e.push_back(node(a1, a2, b2, -1));
s.push_back(a2);
}
discrete();
build(1, 1, m);
for (int i = 0; i < e.size() - 1; i++) {
int l = query(e[i].l), r = query(e[i].r) - 1;
update(1, l, r, e[i].d);
if (i != e.size() - 1) ans += t[1].sum * (e[i + 1].h - e[i].h);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++num, ans);
}
return 0;
}