HDU 5820 Lights(可持久化线段树)

Lights

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1075    Accepted Submission(s): 283


Problem Description
Today is April 1st, 2100. Now Guangzhou is a very very big city. Since the number of traffic accidents increased last month, the mayor asked Mr. Chopsticks to investigate the traffic condition of the city.

After studying the map of Guangzhou for a while, Mr. Chopsticks has some ideas. Guangzhou can be considered as a rectangular grid with 50000 horizontal streets running west-east (labeled with y-coordinates from 1 to 50000) and 50000 vertical streets running north-south (labeled with x-coordinates from 1 to 50000). All streets are two-way streets. A crossroad is an intersection of a horizontal street and a vertical street, so a crossroad can be represented by (x, y), where x and y are coordinates of the horizontal street and vertical street respectively. Since there are too many streets, traffic lights are not placed at all crossroads. Given two crossroads (x1, y1) and (x2, y2), a path between these two crossroads is said to be good if the length of the path is |x1 – x2| + |y1 – y2| and there exist a traffic light at each turn of this path. Of course, a path must be along the streets, and a turn can only be at a crossroad.

Now given locations of all traffic lights in Guangzhou, Mr. Chopsticks wants to check whether there exists at least one good path between every pair of traffic lights. As Mr. Chopsticks is busy in preparing ACMICPC 2100, he asks you for help.
 

Input
The input contains multiple test cases. Each case begins with an integer N (1 <= N <= 500000), indicating the number of traffic lights. The following N lines each contain two integers x and y (1 <= x, y <= 50000), indicating a location of a traffic light. There may be multiple traffic lights at the same location.

N = 0 indicates the end of the input.
 

Output
For each case, output “YES” if there exists at least one good path between every pair of traffic lights; otherwise output “NO”.
 

Sample Input
 
   
2 1 1 3 3 3 1 1 1 3 3 3 0
 

Sample Output
 
   
NO YES
 

Author
SYSU
 

Source


        大致题意:有N个路灯,以坐标的形式给出每个路灯的位置,坐标区间是[1,50000]。现在问你,任意两个路灯之间是否存在一条路径,使得路径总长是两灯的曼哈顿距离,同时路径的转折点处(若有)是一个路灯。输出YES/NO。

        有最多5e10个点,如果暴力寻找是否存在路径显然是不行的。那么我们转而看看,如果满足存在这样的一条路径应该满足什么样的条件。我们注意到,如果两个不共线的点能够相互到达,那么一定能构成一个矩形。相反,就是一定不能构成一个矩形。可以证明,对于点(x,y),在水平方向离它最近的点为(x',y),在垂直方向离它最近的点为(x,y'),那么(x,y)点一定不能够到达这三个点组成的矩形的内部的点。这个自己画一画图就可以理解。那么问题就变成了,对于每一个点,判断与之关联的矩形中是否有其他点存在。
        现在的问题就变成了,怎么实现这个功能。我们考虑用可持久化线段树,可能会问这种二维的问题怎么用可持久化线段树呢?一般来说,求矩形区间和的东西应该是考虑用二维树状数组。但是这题有50W个点,然后坐标区间是5W,显然二维树状数组O(N^2)的空间复杂度是不可承受的。那么又如何考虑用主席树呢?我们知道,可持久化线段树可以知道之前任意一个版本的线段树,那么对于点(x',y')和(x,y)组成的矩形,如果我是按照行列顺序添加节点,(x,y)版本的线段树在区间[y',y]的和减去(x'-1,y')版本的线段树在区间 [y',y]的和就是矩形区间的和。因为后一个版本的线段树包含了所有后加入的点,前一个只包括了之前的点,再加上区间的限制可以求出矩形区间的的和。还是一样画图可以更好的理解。
        解决了统计的问题,我们要做的就是维护一下距离当前节点在水平和垂直方向最近的点了,这个用数组保存即可。还有一点需要注意,我们按照横纵坐标小到大排序,对每个点判断一个矩形,但是并不是判断一次就行了。我们考虑一组数据(1,1)(6,1)(1,2)(6,2)(3,3),这组数据如果按照小到大顺序判断一次结果是正确的。但是我们发现(3,3)点并不能到(6,2)点,应该是错误的。问题在于我们加入点并判断的时候方向是从下到上的,对于特殊的数据会漏掉。于是我们可以考虑把坐标反过来,再次计算一次,两次都正确才是正确。最后就是注意,主席树的空间一定要开大……具体见代码:
#include<bits/stdc++.h>
#define N 500010
using namespace std;

int mx[N],my[N],rt[N<<4],tot;
bool flag;

struct Persistent_SegTree
{
    struct node{int l,r,sum;} T[N<<4];
    int cnt; void init(){cnt=0;}

    void build(int &i,int l,int r)
    {
        i=++cnt;
        T[i]=node{0,0,0};
        if (l==r) return;
        int mid=(l+r)>>1;
        build(T[i].l,l,mid);
        build(T[i].r,mid+1,r);
    }

    void update(int &i,int old,int l,int r,int L,int R,int x)
    {
        i=++cnt;
        T[i]=T[old];
        T[i].sum++;
        if (l==L&&r==R) return;
        int mid=(L+R)>>1;
        if (r<=mid) update(T[i].l,T[old].l,l,r,L,mid,x);
        else if (l>mid) update(T[i].r,T[old].r,l,r,mid+1,R,x);
    }

    int getsum(int i,int l,int r,int L,int R)
    {
        int mid=(L+R)>>1;
        if (l>r) return 0;
        if (l==L&&R==r) return T[i].sum;
        if (r<=mid) return getsum(T[i].l,l,r,L,mid);
        else if (l>mid) return getsum(T[i].r,l,r,mid+1,R);
        else return getsum(T[i].l,l,mid,L,mid)+getsum(T[i].r,mid+1,r,mid+1,R);
    }

} Persist;

struct node
{
    int x,y;

    bool operator ==(const node &a)const
    {
        return a.x==x&&a.y==y;
    }
} p[N];

bool cmp(node a,node b)
{
    return a.x==b.x?a.y<b.y:a.x<b.x;
}

void solve()
{
    if (!flag) return;
    Persist.init();
    memset(mx,0,sizeof(mx));
    memset(my,0,sizeof(my));
    Persist.build(rt[0],1,50000);
    int ls=rt[0];
    for(int i=1;i<=tot;i++)
    {
        int x1=p[i].x,y1=p[i].y;
        int x2=p[my[y1]].x,y2=p[mx[x1]].y;
        Persist.update(rt[x1],ls,y1,y1,1,50000,1);
        int l=Persist.getsum(rt[x2],y2+1,y1-1,1,50000);
        int r=Persist.getsum(rt[x1],y2+1,y1-1,1,50000);
        if (l!=r) {flag=0; return;} ls=rt[x1]; my[y1]=i; mx[x1]=i;
    }
}

int main()
{
    int n,q;
    while(~scanf("%d",&n))
    {
        if (n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%d%d",&p[i].x,&p[i].y);
        flag=1;
        sort(p+1,p+1+n,cmp);
        tot=unique(p+1,p+1+n)-p-1;
        solve();
        for(int i=1;i<=tot;i++) p[i].x=50001-p[i].x;
        sort(p+1,p+1+tot,cmp);
        solve();
        if (flag) puts("YES"); else puts("NO");
    }
    return 0;

}
/*
5
1 1
1 2
1 3
2 1
3 3
*/

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/80355950