JZOJsenior3488.【NOIP2013模拟联考11】矩形(rect)

来源:互联网 发布:js set array 转换 编辑:程序博客网 时间:2024/06/05 10:45

problem

Description

因为对polo忍无可忍, dzf使用圣剑在地上划出了许多纵横交错的沟壑来泄愤。这些沟壑都严格与X轴平行或垂直。

polo嘲笑了dzf无聊的行为,然后做了一件更加无聊的事。他蹲下来数这些沟壑的条数。数着数着,polo意识到一个问题,那就是因为圣剑的威力太大,划出的沟壑太多,地面就会塌陷。而如果两条水平的沟壑和两条垂直的沟壑相交组成了一个矩形,那么塌陷的危险就会进一步增加。现在polo已经数了n条沟壑,他想知道这些沟壑组成了多少个矩形。

Input

第一行一个数n,接下来每行4个数x1,y1,x2,y2,表示沟壑的两个端点(x1,y1),(x2,y2)

Output

一个数,组成的矩形个数。

Sample Input

输入1:

4

0 0 1 0

0 0 0 1

1 1 1 -1

1 1 0 1

输入2:

8

1 0 4 0

2 1 2 0

0 0 0 3

2 2 2 3

3 3 3 -1

0 3 4 3

4 1 -1 1

3 2 -1 2

Sample Output

输出1:

1

输出2:

6

Data Constraint

对于30%的数据,1<=n<=100

对于60%的数据,1<=n<=600

对于100%的数据,1<=n<=2000,坐标绝对值小于10^9,任意两条与X轴水平的沟壑之间没有交点,任意两条与X轴垂直的沟壑没有交点。


analysis

  • 很经典的一道求平面内矩形数量的题目

  • 但是我要插入一点东西


奇怪的东西:水法?

  • 40%的数据O(n4)枚举两条水平和两条垂直线段,傻子都会

  • 60%的数据,也很好想:枚举两条垂直的线段,枚举一条水平的线段

  • 设都与两条垂直线段相交的水平线段的数量为k,那么明显能构成C(k,2)=k(k1)2个矩形

  • 结果一堆人用O(n3)水法切掉了……切掉了……

  • 下面来看看这奇妙的水法代码

水法code

#include<cstdio>struct rec{int u,v,x,y;}a[2001],b[2001];bool pd[2001][2001];int n,u,v,x,y,o,p,f,w[2001][2001],c[2001];__attribute__((optimize("-O3")))bool in(int l,int x,int r){return l<=x&&x<=r;}__attribute__((optimize("-O3")))main(){    scanf("%d",&n);    int i;    for(i=1;i<=n;i++){        scanf("%d%d%d%d",&u,&v,&x,&y);        if(y==v){//V == Y  YZ            if(u>x)f=u,u=x,x=f;            a[++o]=rec{u,v,x,y};        }else{            if(v>y)f=v,v=y,y=f;            b[++p]=rec{u,v,x,y};//U == X  XZ        }    }    int j,k,s=0,cnt,t0,t1;    for(i=1;i<=o;i++)        for(k=1;k<=p;k++)            if(in(a[i].u,b[k].u,a[i].x)&&in(b[k].v,a[i].v,b[k].y)){                pd[i][k]=1;                w[i][++c[i]]=k;            }    for(i=1;i<o;i++)        for(j=i+1;j<=o;j++){            cnt=0;            t0=i,t1=j;            if(c[i]>c[j])t1=i,t0=j;            for(k=1;k<=c[t0];k++)                if(pd[t1][w[t0][k]])cnt++;            s+=cnt*(cnt-1)/2;        }    printf("%d",s);}

哪位大爷看懂了,记得在评论区和我说一声啊


来点正常的吧

  • 正解乃线段树

  • 首先枚举一条垂直线段,把所有和这条线段相交的水平线段加进线段树

  • 怎么加进线段树呢?把水平线段的y坐标离散化,从小到大排个序

  • 把第i号打上一个标记,表示第i条水平线段与当前垂直线段相交

  • 然后我们本来要用一个O(n)循环来找k的,现在用线段树的区间求和O(log2n)求解k

  • 时间复杂度O(n2log2n)

  • 虽说n2000,但分开枚举水平垂直线段,时间达不到那么高

  • 所以线段树能在时间范围内跑出解


线段树code

#include<bits/stdc++.h>#define MAXN 2001using namespace std;int f[5*MAXN],li[5*MAXN],tree[5*MAXN];int n,x,n1,n2,len,tot;long long ans;struct information{    int x1,y1,x2,y2;}a[MAXN],b[MAXN],c[MAXN];bool cmp(information a,information b){    return a.x1<b.x1; }bool cmp1(information a,information b){    return a.x2<b.x2;}int query(int t,int l,int r,int x,int y){    if(x<=l && r<=y)     {        return tree[t];    }    int mid=(l+r)/2;    if(y<=mid)     {        return query(2*t,l,mid,x,y);    }    else if(x>mid)     {        return query(2*t+1,mid+1,r,x,y);    }    else     {        return query(2*t,l,mid,x,mid)+query(2*t+1,mid+1,r,mid+1,y);    }}void change(int t,int l,int r,int x,int y){    if(l==r)     {        tree[t]+=y;         return;    }    int mid=(l+r)/2;    if(x<=mid)     {        change(2*t,l,mid,x,y);      }    else     {        change(2*t+1,mid+1,r,x,y);    }    tree[t]=tree[2*t]+tree[2*t+1];}int search(int t,int l,int r){    int mid=(l+r)/2;    if(f[mid]==t)     {        return li[mid];    }    if(t>f[mid])     {        return search(t,mid+1,r);     }    else     {        search(t,l,mid-1);    }}void init() {    for(int i=1;i<=n1;i++)     {        f[++tot]=a[i].y1;        f[++tot]=a[i].y2;    }    for(int i=1;i<=n2;i++)     {        f[++tot]=b[i].y1;        f[++tot]=b[i].y2;    }    sort(f+1,f+tot+1);    x=li[1]=1;    for(int i=2;i<=tot;i++)     {        if(f[i]!=f[i-1])x++;        li[i]=x;    }    for(int i=1;i<=n1;i++)    {        a[i].y1=search(a[i].y1,1,tot);        a[i].y2=search(a[i].y2,1,tot);    }    for(int i=1;i<=n2;i++)     {        b[i].y1=search(b[i].y1,1,tot);        b[i].y2=search(b[i].y2,1,tot);    }}int find(int t,int l,int r){    if(l>r)return l;    int mid=(l+r)/2;    if(t>c[mid].x2)     {        return find(t,mid+1,r);    }    else     {        return find(t,l,mid-1);    }}long long clam(long long x){    return x*(x-1)/2;}int main(){    //freopen("readin.txt","r",stdin);    scanf("%d",&n);    for(int i=1;i<=n;i++)     {        int x1,y1,x2,y2;        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);        if(x1==x2)        {            a[++n1]={x1,y1,x2,y2};            if(a[n1].y1>a[n1].y2)swap(a[n1].y1,a[n1].y2);        }        else        {            b[++n2]={x1,y1,x2,y2};            if(b[n2].x1>b[n2].x2)swap(b[n2].x1,b[n2].x2);        }    }    sort(a+1,a+n1+1,cmp);    init();     for(int i=1;i<=n1;i++)     {        len=0;         memset(tree,0,sizeof(tree));        for(int j=1;j<=n2;j++)         if(a[i].y1<=b[j].y1 && b[j].y1<=a[i].y2 && b[j].x1<=a[i].x1 && a[i].x1<=b[j].x2)          {            c[++len]={b[j].x1,b[j].y1,b[j].x2,b[j].y2};            change(1,1,x,c[len].y1,1);         }        sort(c+1,c+len+1,cmp1);         int last=1;         for(int j=i+1;j<=n1;j++)         if(a[j].x1>a[i].x1)         {            int t=find(a[j].x1,last,len);             for(int k=last;k<t;k++)            {                change(1,1,x,c[k].y1,-1);             }            last=t;            long long s=0;            int up=min(a[i].y2,a[j].y2),down=max(a[i].y1,a[j].y1);            if(down<=up)            {                s=query(1,1,x,down,up);             }            ans+=clam(s);         }    }    printf("%lld\n",ans);    return 0;}
原创粉丝点击