#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=50000+5;
int sum[maxn<<2];
更新父节点
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //父节点的区间值是两个子节点的和
}
建树
void build(int rt,int l,int r)
{
if(l==r)
scanf("%d",&sum[rt]); //在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
else
{
build(lson); //递归
build(rson);
pushup(rt); //更新父节点
}
}
区间查询
int query(int rt,int l,int r,int L,int R) //初始lr是1和n,LR是区间所以会进入else里,最后返回的是一个凑的值
{
if(L<=l&&r<=R)
return sum[rt]; //递归停止的地方
else
{
int temp=0;
if(L<=m)
temp+=query(lson,L,R);
if(R>m)
temp+=query(rson,L,R);
return temp;
}
}
区间更改(单点更改)
void update(int rt,int l,int r,int L,int R) //L是点,R是它增加或减少的值
{
if(l==r&&r==L)
sum[rt]+=R; //先给最底层更新,然后自下而上更新
else
{
if(L<=m)
update(lson,L,R);
else
update(rson,L,R);
pushup(rt);
}
}
int main()
{
int t,n,a,b;
char ha[10];
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d",&n); //输入个数
build(1,1,n); //直接建树,在建树时为点赋初值
printf("Case %d:\n",i);
while(~scanf("%s",ha))
{
if(ha[0]=='E')
break;
scanf("%d %d",&a,&b);
if(ha[0]=='Q') //查询
printf("%d\n",query(1,1,n,a,b));
else if(ha[0]=='A') //增加
update(1,1,n,a,b);
else //减少
update(1,1,n,a,-b);
}
}
}
与上一题一样,就是求和公式改为最大值公式...
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=200000+5;
int Max[maxn<<2];
void pushup(int rt)
{
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); //父节点的区间值是两个子节点的和
}
void build(int rt,int l,int r)
{
if(l==r)
scanf("%d",&Max[rt]); //在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
else
{
build(lson); //递归
build(rson);
pushup(rt); //更新父节点
}
}
int query(int rt,int l,int r,int L,int R) //初始lr是1和n,LR是区间所以会进入else里,最后返回的是一个凑的值
{
if(L<=l&&r<=R)
return Max[rt]; //递归停止的地方
else
{
int temp=-maxn-500;
if(L<=m)
temp=max(temp,query(lson,L,R));
if(R>m)
temp=max(temp,query(rson,L,R));
return temp;
}
}
void update(int rt,int l,int r,int L,int R) //L是点,R是它增加或减少的值
{
if(l==r&&r==L)
Max[rt]=R; //先给最底层更新,然后自下而上更新
else
{
if(L<=m)
update(lson,L,R);
else
update(rson,L,R);
pushup(rt);
}
}
int main()
{
int n,t,a,b;
char ha[10];
while(~scanf("%d %d",&n,&t))
{
build(1,1,n); //直接建树,在建树时为点赋初值
for(int i=0; i<t; i++)
{
scanf("%s %d %d",ha,&a,&b);
if(ha[0]=='Q') //查询
printf("%d\n",query(1,1,n,a,b));
else
update(1,1,n,a,b);
}
}
}
求区间内ax*ay的最小值,注意x可以等于y!
同时求最大值最小值,最后根据正负情况判断使用最大值还是最小值
1 如果最大值最小值都是正数,用最小值*最小值
2 如果两个数都是负数,用最大值*最大值
3 如果一正一负,那就是这两个数的乘积
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=200000+5;
ll Max[maxn<<2],Min[maxn<<2],sum;
void pushup(ll rt)
{
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); //父节点的区间值是两个子节点的和
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
void build(ll rt,ll l,ll r)
{
if(l==r)//在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
{
scanf("%lld",&Max[rt]);
Min[rt]=Max[rt];
}
else
{
build(lson); //递归
build(rson);
pushup(rt); //更新父节点
}
}
ll query(ll rt,ll l,ll r,ll L,ll R) //区间最大值
{
if(L<=l&&r<=R)
return Max[rt]; //递归停止的地方
ll temp=-maxn-500;
if(L<=m)
temp=max(temp,query(lson,L,R));
if(R>m)
temp=max(temp,query(rson,L,R));
return temp;
}
ll queryy(ll rt,ll l,ll r,ll L,ll R) //区间最小值
{
if(L<=l&&r<=R)
return Min[rt]; //递归停止的地方
ll temp=maxn+500;
if(L<=m)
temp=min(temp,queryy(lson,L,R));
if(R>m)
temp=min(temp,queryy(rson,L,R));
return temp;
}
void update(ll rt,ll l,ll r,ll L,ll R) //L是点,R是它增加或减少的值
{
if(l==r&&r==L)
{
Max[rt]=R;
Min[rt]=R;
} //先给最底层更新,然后自下而上更新
else
{
if(L<=m)
update(lson,L,R);
else
update(rson,L,R);
pushup(rt);
}
}
int main()
{
ll n,t,p,a,b,c;
char ha[10];
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
ll j=1;
for(ll i=0; i<n; i++)
j*=2;
n=j;
sum=0;
build(1,1,n); //直接建树,在建树时为点赋初值
scanf("%lld",&p);
for(ll i=0; i<p; i++)
{
scanf("%lld %lld %lld",&c,&a,&b);
if(c==1) //查询
{
ll x=queryy(1,1,n,a+1,b+1),y=query(1,1,n,a+1,b+1);
if(x<0&&y<0)
printf("%lld\n",y*y);
else if(x<0&&y>=0)
printf("%lld\n",x*y);
else
printf("%lld\n",x*x);
}
else
update(1,1,n,a+1,b);
}
}
}
其实这道题用到线段数的部分就是求原逆反数,之后变化的逆反数是有规律的,直接按规律来求就好了
我们可以这样想:逆反数就是找x的前面有没有比他大的数,如果有y个,那么x的逆反数就是y,这样算好每一个数有多少个逆反数,就可以推规律了。首先,每个开头的数字的逆反数是0,但是它会成为后面数字的逆反数,也就是后面的比他小的数字的逆反数,这个数量是x,把它加到末尾,那么前面比他大的数字的个数就是n-x+1;例如,2在最开始的时候后面比他小的有0,1,逆反数是2,在末位的时候前面比他大的就是3,4,5,6,7,8,9,逆反数是7
规律也有啦,但是用线段树怎么找到原始序列的逆反数呢?
正如我们前面说的那样,找前面出现过的比他小的数量,这时候我们应该建一棵空树,每输入一个数进行一次查询,查询x->n-1中有没有出现过的数字(在这个数字之前出现),查询完后给这个数的值更新为1代表出现过,出现过就计算出现了几个。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=5000+5;
int sum[maxn<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //最大值和最小值
}
void build(int rt,int l,int r)
{
sum[rt]=0;
if(l==r)//在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
return;
else
{
build(lson); //递归
build(rson);
pushup(rt); //更新父节点
}
}
int query(int rt,int l,int r,int L,int R) //初始lr是1和n,LR是区间所以会进入else里,最后返回的是一个凑的值
{
if(L<=l&&r<=R)
return sum[rt]; //递归停止的地方
int temp=0;
if(L<=m) //精华
temp+=query(lson,L,R);
if(R>m)
temp+=query(rson,L,R);
return temp;
}
void update(int rt,int l,int r,int L) //更新,这个函数就是为了查找到最低层然后更新
{
if(l==r&&r==L)
sum[rt]++; //先给最底层更新,然后自下而上更新
else
{
if(L<=m)
update(lson,L);
else
update(rson,L);
pushup(rt);
}
}
int main()
{
int n,a[5005],ans=0;
while(~scanf("%d",&n))
{
build(1,0,n-1); //建空树
for(int i=0; i<n; i++)
{
scanf("%d",&a[i]);
ans+=query(1,0,n-1,a[i],n-1); //范围a[i]->n-1;
update(1,0,n-1,a[i]);
}
int minn=ans;
for(int i=0; i<n; i++) //规律
{
ans=ans-a[i]+n-a[i]-1;
if(ans<minn)
minn=ans;
}
printf("%d\n",minn);
}
}
区间更新
鱼钩上连续的金属棒编号,从1到n。每一次操作,Pudge都可以将X到Y编号的金属棒,变成铜棒、银棒或金棒。挂钩的总价值是用N根金属棒的总价值来计算的。更准确地说,每种木棍的价值计算如下:对于每根铜棒,其值为1。每根银棒的价值是2。每根金棒的价值是3。Pudge想知道执行操作后钩子的总价值。你可以认为最初的钩子是由铜棒组成的
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string.h>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=100000+5;
int sum[maxn<<2],add[maxn<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //最大值和最小值
}
void build(int rt,int l,int r)
{
if(l==r)//在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
sum[rt]=1;
else
{
build(lson); //递归
build(rson);
pushup(rt); //更新父节点
}
}
void pushdown(int rt,int len)
{
if(add[rt])
{
add[rt<<1]=add[rt];
add[rt<<1|1]=add[rt];
sum[rt<<1]=add[rt]*(len-(len>>1));
sum[rt<<1|1]=add[rt]*(len>>1);
add[rt]=0;
}
}
int query(int rt,int l,int r,int L,int R) //初始lr是1和n,LR是区间所以会进入else里,最后返回的是一个凑的值
{
if(L<=l&&r<=R)
return sum[rt];//递归停止的地方
pushdown(rt,r-l+1);
int temp=0;
if(L<=m) //精华
temp+=query(lson,L,R);
if(R>m)
temp+=query(rson,L,R);
return temp;
}
void update(int rt,int l,int r,int L,int R,int Z) //更新,这个函数就是为了查找到最低层然后更新
{
if(L<=l&&r<=R)
{
add[rt]=Z;
sum[rt]=Z*(r-l+1);
return;
}
pushdown(rt,r-l+1);
if(L<=m)
update(lson,L,R,Z);
if(R>m)
update(rson,L,R,Z);
pushup(rt);
}
int main()
{
int n,t,q,a,b,c;
scanf("%d",&t);
for(int op=1; op<=t; op++)
{
scanf("%d",&n);
memset(add,0,sizeof(add));
build(1,1,n);
scanf("%d",&q);
while(q--)
{
scanf("%d %d %d",&a,&b,&c);
update(1,1,n,a,b,c);
}
printf("Case %d: The total value of the hook is %d.\n",op,query(1,1,n-1,1,n-1));
}
}
线段树维护连续区间,开数组sum[],ls[],rs[] 分别表示当前节点最长连续区间长度,以左端点为开端的连续区间长度,以右端点为结束的连续区间长度
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string.h>
using namespace std;
#define ll long long
//注意点的位置要对应,下文都要以rt,l,r为顺序
#define m ((l+r)>>1)
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=100000+5;
//这个代码的连续空间表示为空闲的连续空间,具体可根据题意更改,三个连续空间数组要一起更新
int sum[maxn<<2],add[maxn<<2],ls[maxn<<2],rs[maxn<<2];
void pushdown(int rt,int l, int r)
{
if(add[rt]!=-1)
{
add[rt<<1]=add[rt<<1|1]=add[rt];
if(add[rt]==1) //全部占用,空闲区间长度为0
{
sum[rt<<1]=sum[rt<<1|1]=0;
ls[rt<<1]=ls[rt<<1|1]=rs[rt<<1]=rs[rt<<1|1]=0;
}
else //清空
{
sum[rt<<1]=ls[rt<<1]=rs[rt<<1]=(m-l+1);
sum[rt<<1|1]=ls[rt<<1|1]=rs[rt<<1|1]=(r-m);
}
add[rt]=-1;
}
}
void pushup( int rt,int l, int r)
{
ls[rt] = ls[rt<<1];
rs[rt] = rs[rt<<1|1];
sum[rt] = max(max(sum[rt<<1],sum[rt<<1|1]),rs[rt<<1]+ls[rt<<1|1]);
if(ls[rt<<1]==m-l+1) //如果左子树的全为空闲连续空间,那么树的左连续空间应加上右子树的左连续空间
ls[rt]+=ls[rt<<1|1];
if(rs[rt<<1|1]==r-m) //同上
rs[rt]+=rs[rt<<1];
// printf("%d %d %d %d %d %d %d %d\n",rt,l,r,rs[rt<<1|1],rs[rt<<1],sum[rt],ls[rt],rs[rt]);
}
void build(int rt,int l,int r)
{
if(l==r)//在最底层也就是区间只有一个点的时候赋初值,通过递归把整个树附上初值
sum[rt]=ls[rt]=rs[rt]=1;
else
{
build(lson); //递归
build(rson);
pushup(rt,l,r); //更新父节点
}
}
//这里返回的是符合条件的连续区间的最左边端点
int query(int rt,int l,int r,int x) //初始lr是1和n,LR是区间所以会进入else里,最后返回的是一个凑的值
{
if(l==r)
return l;
pushdown(rt,l,r);
if(sum[rt<<1]>=x) //如果左子树的最大连续区间满足条件
query(lson,x);
else if(rs[rt<<1]+ls[rt<<1|1]>=x) //如果左子树的右连续区间+右子树的左连续区间满足要求,直接返回该连续区间的左端点
return m-rs[rt<<1]+1;
else if(sum[rt<<1|1]>=x) //如果右子树的最大连续区间符合要求
query(rson,x);
}
void update(int rt,int l,int r,int L,int R,int z) //更新,这个函数就是为了查找到最低层然后更新
{
if(L<=l&&r<=R)
{
add[rt]=z;
if(z==1)
sum[rt]=ls[rt]=rs[rt]=0;
else
sum[rt]=rs[rt]=ls[rt]=(r-l+1);
return;
}
pushdown(rt,l,r);
if(L<=m)
update(lson,L,R,z);
if(R>m)
update(rson,L,R,z);
pushup(rt,l,r);
}
int main()
{
int n,t,q,a,b,c;
scanf("%d",&t);
for(int op=1; op<=t; op++)
{
scanf("%d",&n);
memset(add,-1,sizeof(add));
build(1,1,n);
scanf("%d",&q);
while(q--)
{
scanf("%d",&a);
if(a==1)
{
scanf("%d",&b);
if(sum[1]>=b) //先判断
{
int x=query(1,1,n,b);
printf("%d\n",x);
update(1,1,n,x,x+b-1,1);
}
else
printf("-1\n");
}
else
{
scanf("%d %d",&b,&c);
update(1,1,n,b,c,a);
}
}
}
}
扫描线
求面积和
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
const int MAX=2000+10;
int mark[MAX<<2];//记录某个区间的下底边个数
double sum[MAX<<2];//记录某个区间的下底边总长度
double sum1[MAX<<2];//记录出现至少两次的区间下底边长度
double h[MAX];//对x进行离散化,否则x为浮点数且很大无法进行线段树
//以横坐标作为线段(区间),对横坐标线段进行扫描
//扫描的作用是每次更新下底边总长度和下底边个数,增加新面积
struct seg //线段
{
double ll,rr,h;
int d;
seg() {}
seg(double x1,double x2,double H,int c):ll(x1),rr(x2),h(H),d(c) {}
bool operator<(const seg &a)const
{
return h<a.h;
}
} s[MAX];
void pushup(int rt,int l,int r)
{
if(mark[rt])
sum[rt]=h[r+1]-h[l];//表示该区间整个线段长度可以作为底边
else if(l == r)
sum[rt]=0;//叶子结点则底边长度为0(区间内线段长度为0)
else
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
if(mark[rt]>1) //if else 顺序错了会wa
sum1[rt]=h[r+1]-h[l];//表示该区间整个线段长度可以作为底边
else if(l == r)
sum1[rt]=0;//叶子结点则底边长度为0(区间内线段长度为0)
else if(mark[rt]==1)
sum1[rt]=sum[rt<<1]+sum[rt<<1|1];
else
sum1[rt]=sum1[rt<<1]+sum1[rt<<1|1];
}
void update(int rt,int l,int r,int L,int R,int d)
{
if(L<=l && r<=R) //该区间是当前扫描线段的一部分,则该区间下底边总长以及上下底边个数差更新
{
mark[rt]+=d;//更新底边相差差个数
pushup(rt,l,r);//更新底边长
return;
}
if(L<=mid)
update(lson,L,R,d);
if(R>mid)
update(rson,L,R,d);
pushup(rt,l,r);
}
int query(double key,double* x,int n)
{
int l=0,r=n-1;
while(l<=r)
{
int m=(l+r)>>1;
if(x[m] == key)
return m;
if(x[m]>key)
r=m-1;
else
l=m+1;
}
return -1;
}
int main()
{
int n,num=0,t;
double x1,x2,y1,y2,z1,z2;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int k=0;
for(int i=0; i<n; ++i)
{
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
h[k]=x1;
s[k++]=seg(x1,x2,y1,1); //底边
h[k]=x2;
s[k++]=seg(x1,x2,y2,-1);
}
sort(h,h+k);
sort(s,s+k);
int m=1;
for(int i=1; i<k; ++i) //去重复端点
if(h[i] != h[i-1])
h[m++]=h[i];
double ans=0,ans1=0; //ans1是至少出现两次的区间面积,ans是区间面积
memset(sum,0,sizeof (sum));//如果下面是i<k-1则要初始化,因为如果对第k-1条线段扫描时会使得mark,sum为0才不用初始化的
memset(sum1,0,sizeof(sum1));
for(int i=0; i<k; ++i) //扫描线段
{
int L=query(s[i].ll,h,m);
int R=query(s[i].rr,h,m)-1;
update(1,0,m-1,L,R,s[i].d);//扫描线段时更新底边长度和底边相差个数
ans=sum[1]*(s[i+1].h-s[i].h);
ans1+=sum1[1]*(s[i+1].h-s[i].h); //新增加的至少出现两次区间的面积
}
printf("%.2lf %.2lf\n",ans,ans1);
}
return 0;
}
/*
这里注意下扫描线段时r-1:int R=query(s[i].l,h,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=h[r+1]-h[l];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=h[mid]-h[l]+h[r]-h[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,h[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/
扫描线求周长
sum[1] 保存的是当前边长度,当前的边与上一次的边的绝对值就是这次增加的边长度,根据这个定理可以求出横线的周长
竖线当作横线处理
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
const int MAX=2010;
int mark[MAX<<2];//记录某个区间的下底边个数
double sum[MAX<<2];//记录某个区间的下底边总长度
double h[MAX];//对x进行离散化,否则x为浮点数且很大无法进行线段树
double hh[MAX];
//以横坐标作为线段(区间),对横坐标线段进行扫描
//扫描的作用是每次更新下底边总长度和下底边个数,增加新周长
struct seg //线段
{
double ll,rr,h;
int d;
seg() {}
seg(double x1,double x2,double H,int c):ll(x1),rr(x2),h(H),d(c) {}
bool operator<(const seg &a)const
{
return h<a.h;
}
} s[MAX],ss[MAX];
void pushup(int rt,int l,int r,double* x)
{
if(mark[rt])
sum[rt]=x[r+1]-x[l];//表示该区间整个线段长度可以作为底边
else if(l == r)
sum[rt]=0;//叶子结点则底边长度为0(区间内线段长度为0)
else
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int rt,int l,int r,int L,int R,int d,double* x)
{
// printf("%d %d %d^\n",l,r,mid);
if(L<=l && r<=R) //该区间是当前扫描线段的一部分,则该区间下底边总长以及上下底边个数差更新
{
mark[rt]+=d;//更新底边相差差个数
pushup(rt,l,r,x);//更新底边长
return;
}
if(L<=mid)
update(lson,L,R,d,x);
if(R>mid)
update(rson,L,R,d,x);
pushup(rt,l,r,x);
}
int er(double key,double* x,int n)
{
int l=0,r=n-1;
while(l<=r)
{
int m=(l+r)>>1;
if(x[m] == key)
return m;
if(x[m]>key)
r=m-1;
else
l=m+1;
}
return -1;
}
int main()
{
int n,t;
double x1,x2,y1,y2;
cin>>t;
while(t--)
{
memset(sum,0,sizeof(sum));
cin>>n;
int k=0,kk=0;
for(int i=0; i<n; ++i)
{
cin>>x1>>y1>>x2>>y2;
h[k]=x1;//横线
s[k++]=seg(x1,x2,y1,1);
h[k]=x2;
s[k++]=seg(x1,x2,y2,-1);
hh[kk]=y1; //竖线
ss[kk++]=seg(y1,y2,x1,1);
hh[kk]=y2;
ss[kk++]=seg(y1,y2,x2,-1);
}
sort(h,h+k);
sort(s,s+k);
int m=1;
for(int i=1; i<k; ++i) //去重复端点
if(h[i] != h[i-1])
h[m++]=h[i];
double ans=0;
double num=0;
for(int i=0; i<k; ++i) //扫描线段
{
int L=er(s[i].ll,h,m);
int R=er(s[i].rr,h,m)-1;
update(1,0,m-1,L,R,s[i].d,h);//扫描线段时更新底边长度和底边相差个数
ans+=fabs(num-sum[1]);//新增加周长
num=sum[1];
}
//竖线
memset(sum,0,sizeof(sum));
sort(hh,hh+kk);
sort(ss,ss+kk);
int mm=1;
for(int i=1; i<kk; ++i) //去重复端点
if(hh[i] != hh[i-1])
hh[mm++]=hh[i];
num=0;
memset(mark,0,sizeof mark);
memset(sum,0,sizeof(sum));//如果下面是i<k-1则要初始化,因为如果对第k-1条线段扫描时会使得mark,sum为0才不用初始化的
for(int i=0; i<kk; ++i) //扫描线段
{
int L=er(ss[i].ll,hh,mm);
int R=er(ss[i].rr,hh,mm)-1;
update(1,0,mm-1,L,R,ss[i].d,hh);//扫描线段时更新底边长度和底边相差个数
ans+=fabs(sum[1]-num);//新增加周长
num=sum[1];
}
printf("%.0lf\n",ans);
}
return 0;
}
/*
这里注意下扫描线段时r-1:int R=er(s[i].l,h,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=h[r+1]-h[l];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=h[mid]-h[l]+h[r]-h[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,h[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/