[AHOI2008]矩形藏宝地
解法1:
这道题显然可以转化为一个四维偏序问题的模板,即用 CDQ 套 CDQ 解决
虽然三个 log 比较劣,但是思维难度低。
考虑若矩形 \(j\) 要对矩形 \(i\) 产生贡献,即 $ j$ 包含 \(i\),需要满足\(x_{j2}>x_{i2},y_{j2}>y_{i2},x_{j1}<x_{i1},y_{j1}<y_{i1}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
const int K = 2e6+6;
struct Rectangle{
int a,b,c,d,w,ans,pos;//x1,y1,x2,y2,ans是这个矩形包含的数量,pos即原位置
bool operator <(const Rectangle& x)const{
return a==x.a?(b==x.b?(c==x.c?d<x.d:c<x.c):(b<x.b)):a<x.a;
}
}rec[N],tmpb[N],tmpc[N],tmp[N];
int n,m,a,b,c,d,ans;
int f[N];
struct BIT{
int c[K<<1];
int lowbit(int x){return -x&x;}
void add(int x,int val){for(;x<=K;x+=lowbit(x))c[x]+=val;}
int ask(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
}bit;
void solvec(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
solvec(l,mid);solvec(mid+1,r);
int p1=l,p2=mid+1,ctmp=l;
while(p1<=mid&&p2<=r){
if(tmpb[p1].c<tmpb[p2].c){
if(!tmpb[p1].a)bit.add(tmpb[p1].d,tmpb[p1].w);
tmpc[ctmp++]=tmpb[p1++];
}else{
if(tmpb[p2].a)tmpb[p2].ans+=bit.ask(tmpb[p2].d);
tmpc[ctmp++]=tmpb[p2++];
}
}
while(p1<=mid){
if(!tmpb[p1].a)bit.add(tmpb[p1].d,tmpb[p1].w);
tmpc[ctmp++]=tmpb[p1++];
}
while(p2<=r){
if(tmpb[p2].a)tmpb[p2].ans+=bit.ask(tmpb[p2].d);
tmpc[ctmp++]=tmpb[p2++];
}
for(int i=l;i<=mid;i++)
if(!tmpb[i].a)bit.add(tmpb[i].d,-tmpb[i].w);
for(int i=l;i<=r;i++)
tmpb[i]=tmpc[i];
}
void solveb(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
solveb(l,mid);solveb(mid+1,r);
int p1=l,p2=mid+1,ctmp=l;
while(p1<=mid&&p2<=r){
if(rec[p1].b<rec[p2].b){
tmpb[ctmp]=tmp[ctmp]=rec[p1++];
tmpb[ctmp++].a=0;
}else{
tmpb[ctmp]=tmp[ctmp]=rec[p2++];
tmpb[ctmp++].a=1;
}
}
while(p1<=mid){tmpb[ctmp]=tmp[ctmp]=rec[p1++];tmpb[ctmp++].a=0;}
while(p2<=r){tmpb[ctmp]=tmp[ctmp]=rec[p2++];tmpb[ctmp++].a=1;}
solvec(l,r);
for(int i=l;i<=r;i++){
if(tmpb[i].ans)f[tmpb[i].pos]=1;
rec[i]=tmp[i];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
rec[i]={a,b,2e6-c,2e6-d,1,0,i};
}
sort(rec+1,rec+1+n);
solveb(1,n);
for(int i=1;i<=n;i++)
ans+=f[i];
printf("%d\n",ans);
return 0;
}
** 解法二: **
离散化优化冗余复杂度(坐标范围 2e6)
排序维护 x2 降序,归并维护 y2 降序
现在我们保证了右上角可以包含,
接下来可以用前缀最小值BIT用 x1 的坐标,存 y1 的值
然后树状数组 ask() 与当前 y1 比较
特别需要注意的是对于 BIT 的还原顺序
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
const int INF = 0x3f3f3f3f;
int ans,n,cx,cy;
int numx[N<<1],numy[N<<1];
struct Rectangle{
int x,y,xx,yy,ans;
bool operator<(const Rectangle&b)const{
return xx>b.xx;
}
}a[N],tmp[N];
struct BIT{
int c[N<<1];
void init(){memset(c,0x3f,sizeof(c));}
int lowbit(int x){return -x&x;}
void add(int x,int val){for(;x<=cx;x+=lowbit(x))c[x]=min(c[x],val);}
int ask(int x){int res=INF;for(;x;x-=lowbit(x))res=min(res,c[x]);return res;}
void clear(int x){for(;x<=cx;x+=lowbit(x))c[x]=INF;}
}bit;
void init(){
bit.init();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].xx,&a[i].yy);
numx[++cx]=a[i].x;numx[++cx]=a[i].xx;
numy[++cy]=a[i].y;numy[++cy]=a[i].yy;
}
sort(numx+1,numx+1+cx);sort(numy+1,numy+1+cy);
for(int i=1;i<=n;i++){
a[i].x=lower_bound(numx+1,numx+1+cx,a[i].x)-numx;
a[i].y=lower_bound(numy+1,numy+1+cy,a[i].y)-numy;
a[i].xx=lower_bound(numx+1,numx+1+cx,a[i].xx)-numx;
a[i].yy=lower_bound(numy+1,numy+1+cy,a[i].yy)-numy;
}
sort(a+1,a+1+n);
}
void solve(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
solve(l,mid);solve(mid+1,r);
int p1=l,p2=mid+1,ctmp=l;
while(p1<=mid&&p2<=r){
if(a[p1].yy>a[p2].yy){
bit.add(a[p1].x,a[p1].y);
tmp[ctmp++]=a[p1++];
}else{
if(a[p2].y>bit.ask(a[p2].x))a[p2].ans=1;
tmp[ctmp++]=a[p2++];
}
}
while(p2<=r){
if(a[p2].y>bit.ask(a[p2].x))a[p2].ans=1;
tmp[ctmp++]=a[p2++];
}
for(int i=l;i<p1;i++)bit.clear(a[i].x);
while(p1<=mid)tmp[ctmp++]=a[p1++];
for(int i=l;i<=r;i++)a[i]=tmp[i];
}
void calc(){
for(int i=1;i<=n;i++)
ans+=a[i].ans;
printf("%d\n",ans);
}
int main(){
init();
solve(1,n);
calc();
return 0;
}