codeforces1190D Tokitsukaze and Strange Rectangle 线段树(树状数组)扫描线+计数

网址:http://codeforces.com/problemset/problem/1190/D

题意:

给出平面直角坐标系下的点,使用一个上不封顶的矩形把某些点围住作为一个集合,两个集合不同只要数量不同或者数量相同时存在一个点不同,集合必须非空,求这样的集合的数量。

题解:

扫描线法:对$y$轴自顶向下,对于其中一层$y_i$,有以下$n$个点,坐标是$x_1,x_2,x_3......x_n$,以递增序排列,在某一时候,$x_i$加入序列,$y_i$层之前已经计数的矩形不受影响,增加的是加入了新的点之后可构成的新矩形。所以以这个点作为新点,统计加入前的矩形。为了保证有边界,左边界取$1$,有边界取$x$轴的最大值$+1$。则由计数原理,不同的矩形只要存在一条边界不同即可,点数$=$边数$-1$,故矩形数$=$这个点(左边点数$+1)$$*$$($右边点数$+1)$。由于同一个横坐标的值的点等价,对于纵坐标更小而横坐标相同的点,因为已经计算它上面的点,所以其不能和纵坐标大的点累加,故扫描时直接覆盖而不是累加。

然后是技巧,这个题的的数据范围是$2e5$,所以需要数据结构优化,然后坐标值范围是$1e9$,所以需要离散化。

AC代码:

(线段树)

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#pragma GCC optimize(2)
using namespace std;
struct Point
{
    int x,y;
    bool operator<(const Point &a)const
    {
        return y==a.y?x<a.x:y>a.y;
    }
};
Point p[200005];
int x[200005],y[2000005];
struct Segtree
{
    struct node
    {
        int l,r;
        int v;
    }tr[200005*4];
    void build(int l,int r,int k)
    {
        tr[k].l=l,tr[k].r=r;
        if(l==r)
        {
            tr[k].v=0;
            return;
        }
        int m=(l+r)/2;
        build(l,m,k*2);
        build(m+1,r,k*2+1);
        tr[k].v=tr[k*2].v+tr[k*2+1].v;
    }
    void update(int pos,int k,int val)//单点修改
    {
        if(tr[k].l==tr[k].r)
        {
            tr[k].v=val;
            return;
        }
        int m=(tr[k].l+tr[k].r)/2;
        if(pos<=m)
            update(pos,k*2,val);
        else if(pos>m)
            update(pos,k*2+1,val);
        tr[k].v=tr[k*2].v+tr[k*2+1].v;
    }
    int query(int l,int r,int k)
    {
        if(l>r)
            return 0;
        if(l<=tr[k].l&&r>=tr[k].r)
        {
            return tr[k].v;
        }
        int m=(tr[k].l+tr[k].r)/2;
        //cout<<tr[k].l<<" "<<tr[k].r<<endl;
        int ans=0;
        if(l<=m)
            ans+=query(l,r,k*2);
        if(r>m)
            ans+=query(l,r,k*2+1);
        return ans;
    }
};
Segtree seg;
int ydiv[200005];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i=0;i<n;++i)
    {
        cin>>x[i]>>y[i];
        p[i].x=x[i];
        p[i].y=y[i];
    }
    sort(x,x+n);
    sort(y,y+n);
    int xnew=unique(x,x+n)-x;
    int ynew=unique(y,y+n)-y;
    for(int i=0;i<n;++i)
    {
        p[i].x=lower_bound(x,x+xnew,p[i].x)-x+1;
        p[i].y=lower_bound(y,y+ynew,p[i].y)-y+1;
        //cout<<p[i].x<<" "<<p[i].y<<endl;
    }
    sort(p,p+n);
    //扫描线
    seg.build(1,xnew+1,1);
    int beg=0,cur=0;
    long long sum=0;
    while(beg<n)
    {
        int pos=0;
        while(cur<n&&p[cur].y==p[beg].y)
        {
            seg.update(p[cur].x,1,1);
            ydiv[pos++]=p[cur++].x;
        }
        ydiv[pos]=xnew+1;
        for(int i=0;i<pos;++i)
        {
            long long cntl=seg.query(1,ydiv[i]-1,1);
            long long cntr=seg.query(ydiv[i]+1,ydiv[i+1]-1,1);
            sum+=(cntl+1)*(cntr+1);
        }
        beg=cur;
    }
    cout<<sum<<endl;
    return 0;
}

(树状数组)

注:单点修改时,因为每个横坐标值最多只会同时有一个点(被覆盖),所以已经修改成$1$的地方,再需要修改时,也是覆盖成1,所以直接用一个$vis$数组记录是否已经修改。

#include <iostream>
#include <algorithm>
#include <cstring>
#pragma GCC optimize(2)
using namespace std;
struct Fenwick
{
    bool cntx[200005];
    int c[200005];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int pos,int val,int n)//Containing error
    {
        if(cntx[pos])
            return;
        else
        {
            cntx[pos]=1;
            for(int i=pos;i<=n;i+=lowbit(i))
                c[i]+=val;
        }
    }
    int sum(int pos,int n)
    {
        int sum=0;
        for(int i=pos;i;i-=lowbit(i))
            sum+=c[i];
        return sum;
    }
    int query(int l,int r,int n)
    {
        if(l>r)
            return 0;
        return sum(r,n)-sum(l-1,n);
    }
    void print(int n)
    {
        for(int i=0;i<n;++i)
            cout<<c[i]<<" ";
        cout<<endl;
    }
};
struct Point
{
    int x,y;
    bool operator<(const Point &a)const
    {
        return y==a.y?x<a.x:y>a.y;
    }
};
Point p[200005];
int x[200005],y[200005],ydiv[200005];
Fenwick tr;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i=0;i<n;++i)
    {
        cin>>x[i]>>y[i];
        p[i].x=x[i];
        p[i].y=y[i];
    }
    sort(x,x+n);
    sort(y,y+n);
    int xn=unique(x,x+n)-x;
    int yn=unique(y,y+n)-y;
    for(int i=0;i<n;++i)
    {
        p[i].x=lower_bound(x,x+xn,p[i].x)-x+1;
        p[i].y=lower_bound(y,y+yn,p[i].y)-y+1;
        //cout<<p[i].x<<" "<<p[i].y<<endl;
    }
    sort(p,p+n);
    //扫描线
    memset(tr.c,0,sizeof(tr.c));
    int beg=0,cur=0;
    //max xn+1
    long long sum=0;
    while(beg<n)
    {
        int pos=0;
        while(cur<n&&p[cur].y==p[beg].y)
        {
            tr.update(p[cur].x,1,xn+1);
            ydiv[pos++]=p[cur++].x;
            //tr.print(xn+1);
        }
        ydiv[pos]=xn+1;
        for(int i=0;i<pos;++i)
        {
            long long cntl=tr.query(1,ydiv[i]-1,xn+1);
            long long cntr=tr.query(ydiv[i]+1,ydiv[i+1]-1,xn+1);
            sum+=(cntl+1)*(cntr+1);
        }
        beg=cur;
    }
    cout<<sum<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Aya-Uchida/p/11185312.html