【JZOJ4419】【GDOI2016模擬4.2】hole(四~三維偏序問題)
阿新 • • 發佈:2018-08-21
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(四~三維偏序問題)