xdoj 1174 Links 优先队列+线段树


链接:
Links

题意:
坐标系上有n个塔,对于任意两个塔,如果存在一个平行于坐标轴的矩形仅包含这两个塔,可以将它们连接。(包含的含义是位于矩形内部或其边缘)输出最多能够连接多少对塔。

思路:
我们只要对于每个点(xi,yi),处理y坐标小于yi的点,
对于x坐标小于xi的点,按x坐标递增顺序维护一个y坐标单调递减队列,
对于x坐标大于xi的点,按x坐标递减顺序维护一个y坐标单调递减队列,
统计一下就是答案。
这样做的复杂度为o(n^2)
现在的问题是如何维护(1-当前点)的单调队列的大小。
可以用线段树分割维护单调队列。因为单调队列的右端是变化的左端永远是1。
所以可以先访问线段树的右端,然后记录当前单调队列左端最大值,然后再线段树左端进行二分,合并。
例如线段树是(1-8)现在要求(1-6)的单调队列。(1-6)显然分成(1-4)(5-6)。
我们先访问(5-6),然后记录其单调队列的大小,和其左端的值left。
然后再(1-4)的单调队列中二分left的值,然后去掉小于left的部分,然后再记录。
这种做法复杂度为o(nlogn^2)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long int64;
const int maxn = 100000+10 , maxv = maxn*4 , inf = 2000000000;
int n,m,ys[maxn],Right;
int64 ans;
struct node { int x,y; } v[maxn];
vector <node> t[maxv];
 
void init()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d%d",&v[i].x,&v[i].y);
    ans=0;
}
 
void Sort(int l,int r)
{
    int i=l,j=r;
    node mid=v[rand()%(r-l+1)+l];
    do
    {
        while ( v[i].x<mid.x || ( v[i].x==mid.x && v[i].y<mid.y ) ) i++;
        while ( v[j].x>mid.x || ( v[j].x==mid.x && v[j].y>mid.y ) ) j--;
        if (i<=j) 
            swap(v[i++],v[j--]);
    }
    while (i<=j);
    if (l<j) Sort(l,j);
    if (i<r) Sort(i,r);
}
 
int binary( int x )
{
    int l=1,r=m;
    while (l<r) 
    {
        int mid=(l+r)>>1;
        if ( ys[mid]<x ) l=mid+1; 
        else r=mid;
    }
    return l;
}
 
void pre()
{
    for (int i=1;i<=n;i++) 
        v[i].y*=-1,ys[i]=v[i].y;
    sort(ys+1,ys+1+n);
    m=1;
    for (int i=2;i<=n;i++)
        if (ys[i]!=ys[m]) ys[++m]=ys[i];
 
    Sort(1,n);
    for (int i=0;i<maxv;i++) t[i].clear();
}
 
void ins( int x,int a,int b,int l,int r,int X,int Y )
{
    while ( t[x].size() && t[x][t[x].size()-1].y<=Y )
        t[x].pop_back();
    node v; v.x=X,v.y=Y;
    t[x].push_back(v);
 
    if ( a<=l && r<=b ) return;
    int mid=(l+r)>>1;
    if ( a<=mid ) ins(x+x,a,b,l,mid,X,Y);
    if ( mid<b ) ins(x+x+1,a,b,mid+1,r,X,Y);
}
 
int64 find( int x,int a,int b,int l,int r )
{
    if ( a<=l && r<=b ) 
    {
        int L=0,R=t[x].size()-1;
        if ( R<0 || t[x][R].x<=Right ) return 0;
        while ( L<R )
        {
            int Mid=(L+R)>>1;
            if ( t[x][Mid].x>Right ) R=Mid; 
            else L=Mid+1;
        } 
        Right=t[x][t[x].size()-1].x;
        return t[x].size()-R;
    }
    int mid=(l+r)>>1;
    int64 ans=0;
    if ( mid<b ) ans+=find(x+x+1,a,b,mid+1,r);
    if ( a<=mid) ans+=find(x+x,a,b,l,mid);
    return ans;
}
 
void solve()
{
    for (int i=1;i<=n;i++)
    {
        Right=-inf;
        int rank=binary(v[i].y);
        ans+=find(1,1,rank,1,m);
        ins(1,rank,rank,1,m,v[i].x,rank);
    }
}
 
void del()
{
        Sort(1,n);
        for (int i=1;i<=n;)
        {
                int tot=0;
                while ( i+1<=n && v[i].x==v[i+1].x ) i++,tot++;
                ans-=int64(tot);
                i++;
        }
 
        for (int i=1;i<=n;i++) swap(v[i].x,v[i].y);
 
        Sort(1,n);
        for (int i=1;i<=n;)
        {
                int tot=0;
                while ( i+1<=n && v[i].x==v[i+1].x ) i++,tot++;
                ans-=int64(tot);
                i++;
        }
}
 
int main()
{
        init();
     
        pre();
        solve();
        pre();
        solve();
        del();
         
        printf("%lld
",ans);
 
    return 0;
}
原文地址:https://www.cnblogs.com/nanf/p/xdoj1174.html