線段樹掃描線總結:矩形面積並&面積交&周長交
阿新 • • 發佈:2019-02-10
(有任何問題歡迎留言或私聊 && 歡迎交流討論哦
目錄
面積並
- 把每個矩形分成上下兩條邊,記錄左右端點和高度,從下向上掃描,線段樹維護x軸上的有效長度。
- 每次累加部分面積:
- 矩形下邊標記為1,下邊標記為-1.
- 線段樹每個葉子節點表示的是區間的線段長度。(
表示的是離散化後的標號)- 這樣每次。的時候,,因為你更新的時候右下標減了1,所以計算的時候要把1補回來。
- 為什麼每個節點表示的是這段長度呢?這樣呢?
- 這樣做是為了避免漏掉一段線段的情況,類似於POJ 2528這題的離散化要加一的操作。類似但不完全一樣。
AC程式碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#include<bitset>
#define lson rt<<1
#define rson rt<<1|1
#define lsonl l,mid,rt<<1
#define rsonr mid+1,r,rt<<1|1
#define lowbit(x) (x)&(-(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = (int)1e5 +107;
struct lp{
double l,r,h;
int id;
}seg[N];
bool cmp(const lp &a, const lp &b){
return a.h<b.h;
}
int n, m, tot;
int cnt[N<<2];
double sum[N<<2], all[N];
void push_up(int l,int r,int rt){
if(cnt[rt]) sum[rt] = all[r+1]-all[l];
else if(l==r) sum[rt] = 0;
else sum[rt]=sum[lson]+sum[rson];
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
cnt[rt] += c;
push_up(l,r,rt);
return;
}
int mid = (l+r)>>1;
if(L>mid) update(L,R,c,rsonr);
else if(R<=mid) update(L,R,c,lsonl);
else {
update(L,R,c,lsonl);update(L,R,c,rsonr);
}
push_up(l,r,rt);
}
int main(){
int tc=0;
while(~scanf("%d",&n)&&n){
for(int i=0;i<n;++i){
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
seg[i].l=x1;seg[i].r=x2;seg[i].h=y1;seg[i].id=1;
seg[i+n].l=x1;seg[i+n].r=x2;seg[i+n].h=y2;seg[i+n].id=-1;
all[i]=x1,all[i+n]=x2;
}
m = 2*n;
sort(seg,seg+m,cmp);
sort(all,all+m);
int k=0;
for(int i=1;i<m;++i){
if(all[i]!=all[i-1])all[++k]=all[i];
}
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
double ans = 0;
for(int i=0;i<n*2-1;++i){
int l = lower_bound(all,all+k+1,seg[i].l)-all;
int r = lower_bound(all,all+k+1,seg[i].r)-all;
if(l<r) update(l,r-1,seg[i].id,0,k,1);
ans += sum[1]*(seg[i+1].h-seg[i].h);
}
printf("Test case #%d\n", ++tc);
printf("Total explored area: %.2f\n\n", ans);
}
return 0;
}
面積交
- 把每個矩形分成上下兩條邊,記錄左右端點和高度,從下向上掃描,線段樹維護x軸上的有效長度。
- 每次累加部分面積:
- 矩形下邊標記為1,下邊標記為-1.
- 線段樹每個葉子節點表示的是區間的線段長度。(表示的是離散化後的標號)
- 維護的是出現兩次的區間的長度,維護的和上一題的一樣,維護改段出現次數。
- 這裡需要修改一下函式。細節看程式碼。
AC程式碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#include<bitset>
#define lson rt<<1
#define rson rt<<1|1
#define lsonl l,mid,rt<<1
#define rsonr mid+1,r,rt<<1|1
#define lowbit(x) (x)&(-(x))
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = (int)1e5 +107;
struct lp{
double l,r,h;
int id;
}seg[N];
bool cmp(const lp &a, const lp &b){
return a.h<b.h;
}
int n, m, tot, cnt[N<<2];
double one[N<<2], two[N<<2];
double all[N];
void push_up(int l,int r,int rt){
if(cnt[rt]>=2){
one[rt] = two[rt] = all[r+1]-all[l];
}else if(cnt[rt]==1){
one[rt] = all[r+1]-all[l];
if(l==r)two[rt] = 0;
else two[rt]=one[lson]+one[rson];
}else{
if(l==r)two[rt]=one[rt]=0;
else{
one[rt]=one[lson]+one[rson];
two[rt]=two[lson]+two[rson];
}
}
}
void update(int L,int R,int c,int l,int r,int rt){
if(L<=l&&r<=R){
cnt[rt] += c;
push_up(l,r,rt);
return;
}
int mid = (l+r)>>1;
if(L<=mid)update(L,R,c,lsonl);
if(R>mid)update(L,R,c,rsonr);
push_up(l,r,rt);
}
int main(){
int tim;
scanf("%d",&tim);
while(tim--){
scanf("%d",&n);
for(int i=0;i<n;++i){
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
seg[i].l=x1;seg[i].r=x2;seg[i].h=y1;seg[i].id=1;
seg[i+n].l=x1;seg[i+n].r=x2;seg[i+n].h=y2;seg[i+n].id=-1;
all[i]=x1,all[i+n]=x2;
}
m = 2*n;
sort(seg,seg+m,cmp);
sort(all,all+m);
int k = unique(all, all+m)-all-1;
memset(cnt,0,sizeof(cnt));
memset(one,0,sizeof(one));
memset(two,0,sizeof(two));
double ans = 0;
for(int i=0;i<n*2-1;++i){
int l = lower_bound(all,all+k+1,seg[i].l)-all;
int r = lower_bound(all,all+k+1,seg[i].r)-all;