[蓝桥杯][2017年第八届真题]油漆面积
扫描线。
我们取出\(N\)个矩形的左右边界。若一个矩形的两个对角顶点坐标为\((x_1,y_1)\)和\((x_2,y_2)\),则左边界记为四元组\((x_1,y_1,y_2,1)\),右边界记作四元组$(x_2,y_1,y_2,-1)。把这\(2N\)个四元组按照\(x\)递增排序。
逐一扫描排序后的\(2N\)个四元组,设当前四元组为\((x,y_1,y_2,mark)\)。我们把数组\(tr\)中\(tr[y_1],tr[y_1+1],\cdots,tr[y_2-1]\)这些值都加上\(mark\),相当于覆盖了$[y_1,y_2]这个区间。
值得说明的是,四元组中的\(y_1,y_2\)都是坐标,是一个“点”。我们需要维护的是扫描线上每一段被覆盖的次数及其西长度,对“点”的覆盖次数进行统计是没有意义的。因此,我们把\(tr\)数组中的每个值\(tr[i]\)定义成扫描线上一个区间,即\([i,i+1]\)的覆盖次数,四元组\((x,y_1,y_2)\)对\(tr[y_1 \sim y_2-1]\)产生影响。
const int N=10010;
struct Seg
{
int x,y1,y2;
int mark;
bool operator<(const Seg &W) const
{
return x < W.x;
}
}seg[N<<1];
struct Node
{
int l,r;
int cnt;
int len;
}tr[N<<2];
int n;
int cnt;
void pushup(int u)
{
if(tr[u].cnt) tr[u].len=tr[u].r-tr[u].l+1;
else
{
if(tr[u].l == tr[u].r) tr[u].len=0;//特判叶子结点,叶子结点不能借助左右儿子更新,否则会Segmentation Fault
else tr[u].len=tr[lc].len+tr[rc].len;
}
}
void build(int u,int l,int r)
{
tr[u]={l,r};
if(l == r) return;
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void modify(int u,int l,int r,int mark)
{
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].cnt+=mark;
pushup(u);
}
else
{
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(lc,l,r,mark);
if(r>mid) modify(rc,l,r,mark);
pushup(u);
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
seg[cnt++]={x1,y1,y2,1};
seg[cnt++]={x2,y1,y2,-1};
}
sort(seg,seg+cnt);
build(1,0,10000);
int res=0;
for(int i=0;i<cnt;i++)
{
if(i) res+=tr[1].len*(seg[i].x-seg[i-1].x);
modify(1,seg[i].y1,seg[i].y2-1,seg[i].mark);
}
cout<<res<<endl;
//system("pause");
return 0;
}