扫描线
扫描线
扫描线+离散化+线段树
(图片引用自@kk303的博客)
初始状态:
扫到最下面的线,点更新为红色部分:
扫到下一根线,求出绿色部分的面积,加入答案,更新计数器:
同上,求出黄色部分面积,加入答案:
同上,求出灰色部分面积,加入答案:
同上,求出紫色部分面积,加入答案:
同上,求出蓝色部分面积,加入答案:
扫描到最后一条边了,结束!
面积并
就是扫描线,纯的
写法1(动态开点线段树):
\(code\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 200010
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
x=0;bool flag=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n,x1,x2,y1,y2,ans;
int cnt,X[maxn];
int t[2*maxn],tag[2*maxn],rt,ls[2*maxn],rs[2*maxn];
struct data{
int l;
int r;
int h;
int k;//权值1/-1
}lin[maxn];
bool cmp(data x,data y){
if(x.h==y.h) return x.k>y.k;//********
return x.h<y.h;
}
void pushup(int x,int l,int r){
if(tag[x]) t[x]=X[r+1]-X[l];
else t[x]=t[ls[x]]+t[rs[x]];
}
void update(int &x,int l,int r,int cl,int cr,int k){
if(!x) x=++cnt;
if(cl<=l&&cr>=r){
tag[x]+=k;
pushup(x,l,r);
return ;
}
int mid=(l+r)/2;
if(cl<=mid) update(ls[x],l,mid,cl,cr,k);
if(cr>=mid+1) update(rs[x],mid+1,r,cl,cr,k);
pushup(x,l,r);
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(x1),read(y1),read(x2),read(y2);
X[i*2]=x1,X[i*2-1]=x2;
lin[i*2]=(data){x1,x2,y1,1};
lin[i*2-1]=(data){x1,x2,y2,-1};
}
sort(X+1,X+2*n+1);
sort(lin+1,lin+2*n+1,cmp);
int m=unique(X+1,X+2*n+1)-X-2;//去重
for(int i=1;i<=2*n;i++){
int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
int r=lower_bound(X+1,X+m+1,lin[i].r)-X-1;
//--------------
// cout<<l<<" "<<r<<endl;
// cout<<rt<<endl;
// cout<<t[rt]<<endl;
// cout<<lin[i].h-lin[i-1].h<<endl;
// cout<<"*****"<<endl;
ans+=t[rt]*(lin[i].h-lin[i-1].h);
update(rt,1,m,l,r,lin[i].k);
}
cout<<ans<<endl;
return 0;
}
/*
2
100 100 200 200
150 150 250 255
//
18000
*/
写法2(普通线段树):(下面都采用这种写法,注意空间要开大点)
\(code\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 800010//2*4=8倍空间?
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
x=0;bool flag=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n,x1,x2,y1,y2,ans;
int X[maxn];
int t[2*maxn],tag[2*maxn];
struct data{
int l;
int r;
int h;
int k;//权值1/-1
}lin[maxn];
bool cmp(data x,data y){
if(x.h==y.h) return x.k>y.k;
return x.h<y.h;
}
void pushup(int x,int l,int r){
if(tag[x]) t[x]=X[r+1]-X[l];
else t[x]=t[x*2]+t[x*2+1];
}
void update(int x,int l,int r,int cl,int cr,int k){
if(cl<=l&&cr>=r){
tag[x]+=k;
pushup(x,l,r);
return ;
}
int mid=(l+r)/2;
if(cl<=mid) update(x*2,l,mid,cl,cr,k);
if(cr>=mid+1) update(x*2+1,mid+1,r,cl,cr,k);
pushup(x,l,r);
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(x1),read(y1),read(x2),read(y2);
X[i]=x1,X[i+n]=x2;
lin[i]=(data){x1,x2,y1,1};
lin[i+n]=(data){x1,x2,y2,-1};
}
sort(X+1,X+2*n+1);
sort(lin+1,lin+2*n+1,cmp);
int m=unique(X+1,X+2*n+1)-X-1;
for(int i=1;i<=2*n-1;i++){
int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
int r=lower_bound(X+1,X+m+1,lin[i].r)-X;
if(l<r) update(1,1,m,l,r-1,lin[i].k);
ans+=t[1]*(lin[i+1].h-lin[i].h);
}
cout<<ans<<endl;
return 0;
}
/*
2
100 100 200 200
150 150 250 255
//
18000
*/
也是板子,就不写了
面积交
题目求的情况很好处理,覆盖至少两次的都可以,所以我们用 \(tag\) 来记录该节点表示的区间完全被覆盖。
\(tag \ge 2\) 表示该区间被完全覆盖 \(\ge 2\) 次,这正是我们需要的。 \(tag=1\) 表示被完全覆盖切覆盖长度就是区间长度。 \(tag=0\) 表示没被完全覆盖,但是可能覆盖一部分,所以要算出该区间中被覆盖的长度。
由此我们可以算出被覆盖一次和被覆盖两次的长度,我们用 \(one\) 和 \(two\) 来存。
为什么这里存的只是长度呢?因为由扫描线的原理可得,我们是随着水平线的扫描来一块一块加入答案的,高度直接由相减可得,这里就只记录要加入的矩形的长度即可。
推广一下,若求覆盖3次,4次...也可以这样搞,当然次数太多就不行了。
-
其实样例大概是有误差的,\(7.63->7.62\)
在这上面调一年 -
对于题目中的 注意:本题的输入数据较多,推荐使用 \(scanf\) 读入数据. ,其实 \(cin\) 也是可过的(亲测
-
空间要开够!
别问我怎么知道的
\(code\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 4010//线段树4倍空间!!!
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
x=0;bool flag=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int t,n;
int tag[2*maxn];
double x1,x2,y1,y2,ans;
double X[maxn],one[2*maxn],two[2*maxn],ls[2*maxn],rs[2*maxn];
struct data{
double l;
double r;
double h;
int k;
}lin[2*maxn];
bool cmp(data x,data y){
if(x.h==y.h) return x.k>y.k;
return x.h<y.h;
}
void pre(){
ans=0;
memset(tag,0,sizeof(tag));
memset(one,0,sizeof(one));
memset(two,0,sizeof(two));
}
void pushup(int x,int l,int r){
if(tag[x]>=2) two[x]=one[x]=X[r+1]-X[l];
if(tag[x]==1){
one[x]=X[r+1]-X[l];
if(l==r) two[x]=0;
else two[x]=one[x*2]+one[x*2+1];
}
if(tag[x]==0){
if(l==r) one[x]=two[x]=0;
else{
one[x]=one[x*2]+one[x*2+1];
two[x]=two[x*2]+two[x*2+1];
}
}
}
void update(int x,int l,int r,int cl,int cr,int k){
if(cl<=l&&cr>=r){
tag[x]+=k;
pushup(x,l,r);
return ;
}
int mid=(l+r)/2;
if(cl<=mid) update(x*2,l,mid,cl,cr,k);
if(cr>=mid+1) update(x*2+1,mid+1,r,cl,cr,k);
pushup(x,l,r);
}
signed main(){
read(t);
while(t--){
read(n);
for(int i=1;i<=n;i++){
cin>>x1>>y1>>x2>>y2;
X[i]=x1,X[i+n]=x2;
lin[i]=(data){x1,x2,y1,1};
lin[i+n]=(data){x1,x2,y2,-1};
}
sort(X+1,X+2*n+1);
sort(lin+1,lin+2*n+1,cmp);
pre();
int m=unique(X+1,X+2*n+1)-X-1;
for(int i=1;i<=2*n-1;i++){
int l=lower_bound(X+1,X+m+1,lin[i].l)-X;
int r=lower_bound(X+1,X+m+1,lin[i].r)-X;
//--------------
// cout<<l<<" "<<r<<endl;
// cout<<rt<<endl;
// cout<<two[rt]<<endl;
// cout<<lin[i].h-lin[i-1].h<<endl;
// cout<<"*****"<<endl;
if(l<r) update(1,1,m,l,r-1,lin[i].k);
ans+=two[1]*(lin[i+1].h-lin[i].h);
}
printf("%.2lf\n",ans);
}
return 0;
}
/*
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
//
7.63
0.00
*/
/*
1
5
1 3 5 7
2 3 15 20
2.13 5.64 3 8
1 5 4 6
6 3 10 7
//
29.87
*/
周长并
也可以去Luogu P1856提交
给个图感性理解一下(图片来自@TaoSama的博客)
所以x轴方向和y轴方向上各扫一遍然后加起来即可
-
注意因为统计的是边,每一条边都要扫到,所以要扫到 \(2*n\) 而不是 \(2*n-1\)
-
注意pushup里面的顺序
-
因为边会重复覆盖,所以我们需要向答案中加入的是相邻两次修改的区间覆盖长度差
\(code\)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define maxn 10010//线段树4倍空间!!!
#define int long long
using namespace std;
template<typename T>
inline void read(T &x){
x=0;bool flag=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
int n;
int tag[2*maxn],t[2*maxn];
int x1,x2,y1,y2,m[2],ans;
int X[2][maxn];
struct data{
int l;
int r;
int h;
int k;
}lin[2][2*maxn];
bool cmp(data x,data y){
if(x.h==y.h) return x.k>y.k;
return x.h<y.h;
}
void pre(){
memset(tag,0,sizeof(tag));
memset(t,0,sizeof(t));
}
void pushup(int id,int x,int l,int r){
// if(l==r) t[x]=0;//注意!这样写是错的!想想为什么********
// else{
// if(tag[x]) t[x]=X[id][r+1]-X[id][l];
// else t[x]=t[x*2]+t[x*2+1];
// }
if(tag[x]) t[x]=X[id][r+1]-X[id][l];
else{
if(l==r) t[x]=0;
else t[x]=t[x*2]+t[x*2+1];
}
}
void update(int id,int x,int l,int r,int cl,int cr,int k){
if(cl<=l&&cr>=r){
tag[x]+=k;
pushup(id,x,l,r);
return ;
}
int mid=(l+r)/2;
if(cl<=mid) update(id,x*2,l,mid,cl,cr,k);
if(cr>=mid+1) update(id,x*2+1,mid+1,r,cl,cr,k);
pushup(id,x,l,r);
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
read(x1),read(y1),read(x2),read(y2);
X[0][i]=x1,X[0][i+n]=x2;
X[1][i]=y1,X[1][i+n]=y2;
lin[0][i]=(data){x1,x2,y1,1};
lin[0][i+n]=(data){x1,x2,y2,-1};
lin[1][i]=(data){y1,y2,x1,1};
lin[1][i+n]=(data){y1,y2,x2,-1};
}
sort(X[0]+1,X[0]+2*n+1);
sort(X[1]+1,X[1]+2*n+1);
sort(lin[0]+1,lin[0]+2*n+1,cmp);
sort(lin[1]+1,lin[1]+2*n+1,cmp);
m[0]=unique(X[0]+1,X[0]+2*n+1)-X[0]-1;
m[1]=unique(X[1]+1,X[1]+2*n+1)-X[1]-1;
for(int i=0;i<=1;i++){
int ans_tmp=0,lst=0;
pre();
for(int j=1;j<=2*n;j++){
int l=lower_bound(X[i]+1,X[i]+m[i]+1,lin[i][j].l)-X[i];
int r=lower_bound(X[i]+1,X[i]+m[i]+1,lin[i][j].r)-X[i];
if(l<r) update(i,1,1,m[i],l,r-1,lin[i][j].k);
ans_tmp+=abs(t[1]-lst);
lst=t[1];
}
ans+=ans_tmp;
}
cout<<ans<<endl;
return 0;
}
/*
7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16
//
228
*/
其他题目
-
Get The Treasury
[HDU 3642] Get The Treasury -
Stars in your window
[POJ 2482] Stars in your window
P1502 窗口的星星
\(code\)#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define N 100010 #define int long long using namespace std; template<typename T> inline void read(T &x){ x=0;bool flag=0;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') flag=1; for(;isdigit(c);c=getchar()) x=x*10+(c^48); if(flag) x=-x; } int T,n,w,h; int X[N],t[N],tag[N],tot,ans,pre; struct node{ int x; int y; int l; }s[N]; bool cmp(node a,node b){ if(a.x==b.x) return a.l>b.l; return a.x<b.x; } void pushup(int x){ t[x]=max(t[x*2],t[x*2+1]); } void pushdown(int x){ if(tag[x]){ tag[x*2]+=tag[x];tag[x*2+1]+=tag[x]; t[x*2]+=tag[x];t[x*2+1]+=tag[x]; tag[x]=0; } } void change(int x,int l,int r,int cl,int cr,int k){ if(cl<=l&&cr>=r){ tag[x]+=k; t[x]+=k; return ; } int mid=(l+r)/2; pushdown(x); if(cl<=mid) change(x*2,l,mid,cl,cr,k); if(cr>=mid+1) change(x*2+1,mid+1,r,cl,cr,k); pushup(x); } void build(int x,int l,int r){ t[x]=tag[x]=0; if(l==r) return ; int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); } signed main(){ read(T); while(T--){ tot=ans=0,pre=1; read(n),read(w),read(h); for(int i=1;i<=n;i++){ read(s[i].x),read(s[i].y),read(s[i].l); X[++tot]=s[i].y; } sort(X+1,X+tot+1); sort(s+1,s+n+1,cmp); tot=unique(X+1,X+tot+1)-X-1; build(1,1,tot); for(int i=1;i<=n;i++){ while(s[i].x-s[pre].x>=w){ int l=lower_bound(X+1,X+tot+1,s[pre].y)-X; int r=lower_bound(X+1,X+tot+1,s[pre].y+h)-X-1; change(1,1,tot,l,r,-s[pre].l); pre++; } int l=lower_bound(X+1,X+tot+1,s[i].y)-X; int r=lower_bound(X+1,X+tot+1,s[i].y+h)-X-1; change(1,1,tot,l,r,s[i].l); ans=max(ans,t[1]); } printf("%lld\n",ans); } return 0; } /* 2 3 5 4 1 2 3 2 3 2 6 3 1 3 5 4 1 2 3 2 3 2 5 3 1 // 5 6 */