LOJ6299:「CodePlus 2018 3 月赛」白金元首与克劳德斯 (离散化+前缀和)

题目传送门: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值