又是喜闻乐见的只会傻逼做法的题。。跟我跑得差不多快的人都写了1K,我写了快4K。。
并不知道他们怎么搞的,说下我的做法:
考虑按x从大到小的扫描线,每次在矩形的左下角
(x1,y1)
插入一个数
y2
,判断一个矩形
(x1,y1)−(x2,y2)
是否被包含就相当于询问矩形
(0,0)−(x1,y1)
的最大值是否大于
y2
。就是要支持单点插入,矩形最大值。一个显然比较乱搞的做法就是K-D树。
在k-d树上查询的时候因为是dfs,所以可以各种剪枝。建树的时候可以类似划分树一样建,就是
O(nlogn)
的。。我一开始建的特别暴力,
O(nlog2n)
建树,也没剪枝,T的飞起。。
最后口胡一下科学的做法该怎么搞(其实我感觉kd树就挺科学的。。)。如果我们对扫描线用cdq分治的话,就相当于是有一坨点,然后查询前缀矩形max,这个就可以用扫描线+bit搞出来。时间复杂度就是 O(nlog2n) 。
我看别人都写得特别短。。听说是暴力乱搞。。不知道他们怎么搞的。。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
const int N=2e5+5;
struct KS{
int lx,rx,ly,ry,max;
}kdtree[N<<2];
struct PS{
int x,y;
}point[N];
int a[N],b[N],tmp[N];
int pos[N];
bool cmp1(const int &a,const int &b){
return point[a].x<point[b].x;
}
bool cmp2(const int &a,const int &b){
return point[a].y<point[b].y;
}
const int inf=0x7fffffff;
void build(int node,int depth,int tot){
//printf("---%d,%d---\n",node,tot);
kdtree[node]=(KS){point[a[0]].x,point[a[tot-1]].x,point[b[0]].y,point[b[tot-1]].y,-1};
if(tot==1){
//printf("%d:%d\n",node,a[0].i);
pos[a[0]]=node;
}
else{
/*printf("a=");
for(int i=0;i<tot;++i)printf("%d(%d) ",a[i].i,a[i].x);
puts("");
printf("b=");
for(int i=0;i<tot;++i)printf("%d ",b[i].i);
puts("");*/
//if(rand()&1){
if(depth){
//puts("x!");
int ttot=0;
for(int i=0;i<tot;++i)
if(point[b[i]].x<point[a[tot>>1]].x){
tmp[ttot++]=b[i];
//printf("Get:%d %d %d\n",b[i].i,b[i].x,a[tot>>1].x);
}
for(int i=0;i<tot;++i)
if(point[b[i]].x>=point[a[tot>>1]].x)
tmp[ttot++]=b[i];
memcpy(b,tmp,sizeof(int)*tot);
}
else{
//puts("y!");
int ttot=0;
for(int i=0;i<tot;++i)
if(point[a[i]].y<point[b[tot>>1]].y)
tmp[ttot++]=a[i];
for(int i=0;i<tot;++i)
if(point[a[i]].y>=point[b[tot>>1]].y)
tmp[ttot++]=a[i];
memcpy(a,tmp,sizeof(int)*tot);
}
/*printf("a=");
for(int i=0;i<tot>>1;++i)printf("%d ",a[i].i);
puts("");
printf("b=");
for(int i=0;i<tot>>1;++i)printf("%d ",b[i].i);
puts("");*/
build(node<<1,depth^1,tot>>1);
memcpy(a,a+(tot>>1),sizeof(int)*(tot-(tot>>1)));
memcpy(b,b+(tot>>1),sizeof(int)*(tot-(tot>>1)));
build(node<<1|1,depth^1,tot-(tot>>1));
}
}
void update(int node,int A){
//cout<<"update("<<node<<','<<A<<")\n";
for(;node;node>>=1)kdtree[node].max=max(kdtree[node].max,A);
}
bool query(int node,int rx,int ry,int downlimit){
//printf("%d={lx=%d,rx=%d,ly=%d,ry=%d,max=%d} (%d,%d)\n",node,kdtree[node].lx,kdtree[node].rx,kdtree[node].ly,kdtree[node].ry,kdtree[node].max,rx,ry);
if(kdtree[node].max<downlimit)return 0;
if(kdtree[node].rx<=rx&&kdtree[node].ry<=ry)return kdtree[node].max>downlimit;
else{
if(kdtree[node].lx<=rx&&kdtree[node].ly<=ry){
if(query(node<<1,rx,ry,downlimit))return 1;
if(query(node<<1|1,rx,ry,downlimit))return 1;
}
return 0;
}
}
struct RS{
int x1,y1;
int x2,y2;
int i;
bool operator < (const RS & o)const{
return x2<o.x2;
}
}rec[N];
char * cp=(char *)malloc(10000000);
void in(int &x){
while(*cp<'0'||*cp>'9')++cp;
for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
int main(){
freopen("bzoj_1790.in","r",stdin);
fread(cp,1,10000000,stdin);
int n;
in(n);
for(int i=n;i--;){
in(rec[i].x1),in(rec[i].y1);
in(rec[i].x2),in(rec[i].y2);
rec[i].i=i;
point[i]=(PS){rec[i].x1,rec[i].y1};
b[i]=a[i]=i;
}
sort(a,a+n,cmp1);
sort(b,b+n,cmp2);
build(1,0,n);
//for(int i=n;i--;)printf("pos(%d)=%d\n",i,pos[i]);
sort(rec,rec+n);
int ans=0;
for(int i=n;i--;){
//printf("(%d,%d)-(%d,%d) i=%d\n",rec[i].x1,rec[i].y1,rec[i].x2,rec[i].y2,rec[i].i);
ans+=query(1,rec[i].x1-1,rec[i].y1-1,rec[i].y2);
update(pos[rec[i].i],rec[i].y2);
}
printf("%d\n",ans);
}
总结:
①矩阵/高维问题可以用cdq分治搞一维,这样时间/空间都会比较好。
②k-d树建树的时候可以像划分树一样建,这样就是
O(nlogn)
;查询的时候多搞点剪枝,就会跑得比较快了。