UVA1468 Restaurant

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4214

题解

我几何题做的太少了
首先,到一个点曼哈顿距离相等的点肯定在一个菱形上,且菱形内部的点离这个点的曼哈顿距离更小,菱形外部距离更大
假设旅馆为 A , B ,假设除了 A , B 之外的一个任意点为 p ,那么这个题就是对每一个 p 绘制两个经过它且分别以 A , B 为中心的菱形,这两个菱形的内部区域求并集;再把每一个点并出来的集合取交集,问你最终交集的大小。
这个题 m n 很大, O ( n m ) 或者 O ( n 2 ) 或者 O ( m 2 ) 的算法都是不合适的,因此可能是线性算法或者是带一两个 log 的算法,甚至可能是带一个根号的算法
如果考虑线性复杂度的算法, O ( n ) 似乎并不好想,再考虑 O ( m )
何处找 m
题目要求考虑 A , B 这两个旅馆,把这两个旅馆对应的菱形画出来,取个交集,就会发现所有的点都在以 A , B 为左右端点的菱形内部,那么就可以 O ( m ) 枚举横坐标,纵坐标要考虑出一个小于线性的算法来统计
这里写图片描述
对于每旅馆,它都要再上述大菱形中切去一部分不合法的点,在上图中,每种颜色的细线都代表了它所产生的两个菱形的边界(的一部分),细线所形成直角的内部的区域需要被切掉,这两块区域是对称的
黑色菱形减去被切掉部分就是答案,但是切掉部分的面积不好求
考虑直接求出剩下部分的面积,既然已经枚举了每一个横坐标,那么考虑如何统计每个横坐标上下能抵达的最远点
由于上下对称,我们可以先看上边
上图中每一种颜色的直角都有两条边组成,我把它拆成两个方向的射线,先考虑形如 x + y = z 这种形式的射线
显然一条射线能够影响到的区域如下
这里写图片描述
这就是说,我可以按照横纵坐标之和对射线进行排序,用每一条射线更新它在 A B 线段上的投影这一段区间的纵坐标的最大值,而且一条射线更新过的区间,排在后面的射线不会再影响到这段区间,因此保证每一个横坐标只被一条射线更新,均摊就是 O ( m ) 的,排序是 O ( n l o g n ) 的(其实也可以 O ( m ) )
其它射线的处理类似
总的复杂度 O ( n l o g n + m )

代码

//几何题
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cmath>
#define cl(x) memset(x,0,sizeof(x))
#define maxn 100000
using namespace std;
struct point{int x, y;}pt[maxn];
bool cmp1(point& a, point& b){return a.x+a.y < b.x+b.y;}
bool cmp2(point& a, point& b){return a.y-a.x < b.y-b.x;}
int read(int x=0)
{
    char c, f=1;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
    return f*x;
}
int n, m, L, R, cnt[maxn], Y;
void work()
{
    int i, p, j, ans=0;
    m=read(), n=read();
    for(i=1;i<=n;i++)pt[i].x=read(), pt[i].y=read();
    if(pt[1].x>pt[2].x)swap(pt[1],pt[2]);
    L=pt[1].x, R=pt[2].x, Y=pt[1].y;
    //统计线上
    for(i=L+1;i<R;i++)cnt[i]=min(m,min(i-L,R-i)+Y);
    for(i=3;i<=n;i++)if(pt[i].y<Y)pt[i].y=2*Y-pt[i].y;
    sort(pt+3,pt+n+1,cmp1);
    p=L;
    for(i=3;i<=n;i++)
        for(j=p+1;j<=pt[i].x;j++,p++)cnt[j]=min(cnt[j],pt[i].x+pt[i].y-j);
    sort(pt+3,pt+n+1,cmp2);
    p=R;
    for(i=3;i<=n;i++)
        for(j=p-1;j>=pt[i].x;j--,p--)cnt[j]=min(cnt[j],pt[i].y-pt[i].x+j);
    for(i=L+1;i<R;i++)ans+=cnt[i]-Y;
    //统计线下 
    for(i=L+1;i<R;i++)cnt[i]=max(-1,Y-min(i-L,R-i));
    for(i=3;i<=n;i++)pt[i].y=2*Y-pt[i].y;
    sort(pt+3,pt+n+1,cmp1);
    p=R;
    for(i=n;i>=3;i--)
        for(j=p-1;j>=pt[i].x;j--,p--)cnt[j]=max(cnt[j],pt[i].x+pt[i].y-j);
    sort(pt+3,pt+n+1,cmp2);
    p=L;
    for(i=n;i>=3;i--)
        for(j=p+1;j<=pt[i].x;j++,p++)cnt[j]=max(cnt[j],pt[i].y-pt[i].x+j);
    for(i=L+1;i<R;i++)if(cnt[i]<Y)ans+=Y-1-cnt[i];
    printf("%d\n",ans);
}
int main()
{
    int T=read();
    while(T--)work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81289758