1. 程式人生 > >JZOJsenior3488.【NOIP2013模擬聯考11】矩形(rect)

JZOJsenior3488.【NOIP2013模擬聯考11】矩形(rect)

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 2001

using 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;
}