题目传送门:https://loj.ac/problem/6299
题目分析:一道不难的题目,然而比赛的时候只有90pts。由于所有克劳德斯(clouds)一开始都不相交,所以答案不是1就是2。用 O(n2) O ( n 2 ) 暴力判断两朵云是否能相交,就有80pts了。
接下来可以直接把判断相交的式子化开,用一些数据结构维护一下,就可以做到 O(nlog(n)) O ( n log ( n ) ) ,然而有一种更好的方法。令横向移动的云不动,纵向移动的云看成向左上方移动,我们发现这和原问题的移动方式等价。也就是说如果向左上方斜着看的话,每一朵云其实就是一个区间[x+y+2,x+W+y+H]。用map离散化一下,然后打标记做两次前缀和即可(第一次前缀和是为了正确得出每个位置的云朵数量,再做前缀和则是为了判断一个区间内有没有非0的数)。
这里还有一个很坑的地方:令所有云大小都是1*1,假设某个时刻一朵横向移动的云在(x,y)处,另一朵纵向移动的云在(x,y+1)处,那么在接下来的不到1s内,它们是能相交的,只不过相交部分面积小于1。1s后它们又分开了,这个时候也要算答案为2。故对于所有用来查询答案的云,一开始要先将斜坐标区间左端点-1,右端点+1。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
#define MP(x,y) make_pair(x,y)
const int maxn=100100;
struct data
{
int lx,rx,dy,uy,id;
} ;
data a[maxn];
data b[maxn];
int na,nb;
multimap <int,int> mp;
int val[maxn<<2];
int sum[maxn<<2];
int t,n;
bool Judge()
{
mp.clear();
for (int i=1; i<=na; i++)
mp.insert( MP(a[i].lx+a[i].dy,i) ),mp.insert( MP(a[i].rx+a[i].uy,na+i) );
for (int i=1; i<=nb; i++)
mp.insert( MP(b[i].lx+b[i].dy,2*na+i) ),mp.insert( MP(b[i].rx+b[i].uy,2*na+nb+i) );
int num=0,last=-2e9;
for (multimap <int,int> :: iterator p=mp.begin(); p!=mp.end(); p++)
{
if (p->first>last) num++;
last=p->first;
val[p->second]=num;
}
num+=2;
for (int i=0; i<=num; i++) sum[i]=0;
for (int i=1; i<=na; i++) sum[ val[i] ]++,sum[ val[na+i]+1 ]--;
for (int i=1; i<=num; i++) sum[i]+=sum[i-1];
for (int i=1; i<=num; i++) sum[i]+=sum[i-1];
for (int i=1; i<=nb; i++)
if (sum[ val[2*na+nb+i] ]-sum[ val[2*na+i]-1 ]>0) return true;
return false;
}
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
na=nb=0;
for (int i=1; i<=n; i++)
{
data p;
int x,y;
scanf("%d%d%d%d%d",&p.lx,&p.dy,&x,&y,&p.id);
p.rx=p.lx+x;
p.uy=p.dy+y;
p.lx++;
p.dy++;
if (p.id) a[++na]=p;
else p.dy--,p.uy++,b[++nb]=p;
//考虑擦过去的情况,即移动的时候覆盖重叠面积!!!
//故询问的b数组,斜坐标可以+1-1
}
if ( Judge() ) printf("2\n");
else printf("1\n");
}
return 0;
}