1. 程式人生 > >【JZOJ4419】【GDOI2016模擬4.2】hole(四~三維偏序問題)

【JZOJ4419】【GDOI2016模擬4.2】hole(四~三維偏序問題)

gfs text 限制 滿足 ace mat 計算 完成 triangle

Problem

  • 給出n次事件,每次事件給出三個非負整數x,y,d。d=0表示在點(x,y)打了一個洞;否則表示詢問由(x,y),(x+d,y),(x,y+d)三點圍成的三角形中洞的個數。

    Hint

    30%的數據n<=3333 。
    另30% 的數據 GFS只會在DSJ打完洞後才開始詢問,xi,yi<=333333 。
    100%的數據 1<=n<=88888,xi,yi<=3333333 。

    Solution

    算法I:暴力

  • 考慮對於詢問(i,j,d),若點(x,y)在該詢問的三角形內,需要滿足哪些條件。
  • 應滿足這三個條件:1. i≤x≤i+d ; 2. x+y≤i+j+d ; 3. y≥j 。
  • 那麽,我們可以用一個數組存儲已經出現過的點(打的那些洞),然後掃一遍數組,統計一下有多少個點滿足這三個條件即可。


  • 時間復雜度:\(O(n^2)\)
  • 期望得分:30~100points。
  • xzb的經歷告訴我們:對於這種n比較小的高維偏序問題,暴力是可以碾標算的。我們應屑於打打暴力,萬一過了呢?

    算法II:四維偏序問題

  • 觀察到算法I實際上是在求:滿足上述三個條件,並且出現時間<詢問時間的點數。
  • 那麽就轉化為一個四維偏序問題。
  • 我們可以對時間分治,掃描線再解決一維,最後用一個二維數據結構(譬如帶修主席樹)解決剩下兩維。


  • 時間復雜度:\(O(n*(log_2n)^3)\)
  • 期望得分:60~100points。
  • 友情提醒:如果在比賽中想到了這種十分難打、十分難調,計算器一算極限數據復雜度接近4億的、估計不是正解的算法,最好還是別打。畢竟打掛了,白白浪費時間;就算沒打掛,也可能會T。

    算法III:簡單容斥+二維偏序問題

  • 假設我們打完了算法I,略過了算法II,考慮一下離線的那30points有什麽較為穩妥的方法。
    技術分享圖片
  • 如圖,假設我們要詢問淺藍色\(\triangle\)
  • 實際上,淺藍色\(\triangle\)內點的個數=兩條深藍色斜線內點的個數-左上方平行四邊形內點的個數-右下方平行四邊形內點的個數


  • 將所有詢問拆成兩條x+y的掃描線。(顯然,深藍色斜線上的點的x+y為同一個數)那麽,我們將所有點和詢問按照x+y排序。
  • 對於詢問,可以借鑒差分的思想。就是說,我們在掃到第一條斜線(左下方那條)時,貢獻為點數* (-1);掃到第二條斜線(右上方那條)時,貢獻為點數 *(+1)。這樣,即可求出兩條斜線間點的個數。
  • 至於那兩個平行四邊形,左上方那個要滿足x在一定範圍內,右下方那個要滿足y在一定範圍內。我們用兩個樹狀數組維護即可。

  • 時間復雜度:\(O(n*log_2n)\)
  • 期望得分:60points。

    算法IV:再加一維時間

  • 從算法III我們可發現,如果去掉了時間限制(打洞和詢問的先後關系),我們可以用\(O(n*log_2n)\)的時間完成。
  • 那麽直接套個CDQ分治即可優美地解決時間的一維!


  • 時間復雜度:\(O(n*(log_2n)^2)\)
  • 期望得分:100points。

    Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;

const int N=3e5;
int i,n,x,y,d,xs,X[N],ys,Y[N],m,c1[N],c2[N],ans[N];
struct oper
{
    int x,y,c,v;
    inline bool operator<(const oper a)const {return c<a.c||c==a.c&&abs(v)<abs(a.v);}
}o[N],a[N];

void read(int&x)
{
    char ch=getchar(); x=0;
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
}

inline int lowbit(int x) {return x&-x;}
int query(int *c,int n)
{
    int ans=0;
    for(;n;n-=lowbit(n)) ans+=c[n];
    return ans;
}
void add(int *c,int cs,int k,int d)
{
    for(;k<=cs;k+=lowbit(k)) c[k]+=d;
}

void work(int cnt)
{
    sort(a+1,a+cnt+1);
    int c=0;
    fo(i,1,cnt)
        if(a[i].v)
        {
            int v=abs(a[i].v),f=a[i].v/v;
            ans[v]+=f*( c - query(c1,a[i].x) - query(c2,a[i].y) );
        }
        else
        {
            c++;
            add(c1,xs,a[i].x,1); add(c2,ys,a[i].y,1);
        }
    
    fo(i,1,cnt) if(!a[i].v) add(c1,xs,a[i].x,-1), add(c2,ys,a[i].y,-1);
}
void CDQ(int l,int r)
{
    int m=l+r>>1,h=0,b=0;
    fo(i,l,m) if(!o[i].v) a[++h]=o[i];
    fo(i,i,r) if(o[i].v) a[h+(++b)]=o[i];
    if(h&&b) work(h+b);
    if(h&&h<=m-l) CDQ(l,m);
    if(b&&b<r-m) CDQ(m+1,r);
}

int main()
{
    read(n);
    fo(i,1,n)
    {
        read(x); read(y); read(d);
        if(!d)
        {
            X[++xs]=x; Y[++ys]=y;
            o[++m]={x,y,x+y,0}; ans[i]=-1;
            continue;
        }
        X[++xs]=x-1; Y[++ys]=y-1;
        o[++m]={x-1,y-1,x+y-1,-i};
        o[++m]={x-1,y-1,x+y+d, i};
    }
    
    
    sort(X+1,X+xs+1); xs=unique(X+1,X+xs+1)-X-1;
    sort(Y+1,Y+ys+1); ys=unique(Y+1,Y+ys+1)-Y-1;
    fo(i,1,m) 
    {
        o[i].x=lower_bound(X+1,X+xs+1,o[i].x)-X;
        o[i].y=lower_bound(Y+1,Y+ys+1,o[i].y)-Y;
    }
    
    CDQ(1,m);
    
    fo(i,1,n) if(ans[i]>=0) printf("%d\n",ans[i]);
}

【JZOJ4419】【GDOI2016模擬4.2】hole(四~三維偏序問題)