【KD-TREE介紹】在SYC1999大神的“蠱惑”下,我開始接觸這種算法。
首先,大概的概念可以去百度百科。具體實現,我是看RZZ的代碼長大的。
我們可以想象在平面上有N個點。首先,按橫坐標排序找到最中間的那個點。然後水平劃一條線,把平面分成左右兩個部分。再遞歸調用左右兩塊。注意,在第二次(偶數次)調用的時候,是找到縱坐標中最中間的點,並垂直畫一條線。
這樣效率看上去很好。維護的時候有點像線段樹。每個點記錄它的坐標、它轄管的區間4個方向的極值、它的左右(或上下)的兩個點的標號。遞歸兩個子樹時,注意要up更新這個點轄管的范圍。
inline int cmp(arr a,arr b){return a.d[D]>1; nth_element(a+l+1,a+mid+1,a+r+1,cmp); a[mid].min[0]=a[mid].max[0]=a[mid].d[0]; a[mid].min[1]=a[mid].max[1]=a[mid].d[1]; if (l!=mid) a[mid].l=build(l,mid-1,dd^1); if (mid!=r) a[mid].r=build(mid+1,r,dd^1); if (a[mid].l) up(mid,a[mid].l); if (a[mid].r) up(mid,a[mid].r); return mid; }
上述代碼很好理解。
然後先在我要支持加入點,也是類似於線段樹的思想:
void insert(int k) { int p=root;D=0; while (orzSYC) { up(p,k); if (a[k].d[D]<=a[p].d[D]){if (!a[p].l) {a[p].l=k;return;} p=a[p].l;} else {if (!a[p].r) {a[p].r=k;return;} p=a[p].r;} D^=1; } }
為什麼我忽然覺得是splay的insert操作?就是每次往某個點的左或右(或者上或下)過去。
比如我們要查詢與(x,y)最近的點(曼哈頓距離)與其的距離。
int getdis(int k) { int res=0; if (xa[k].max[0]) res+=x-a[k].max[0]; if (ya[k].max[1]) res+=y-a[k].max[1]; return res; } void ask(int k) { int d0=abs(a[k].d[0]-x)+abs(a[k].d[1]-y); if (d0getdis有點像Astar中的“估價函數”。計算(x,y)與當前點范圍的差距有多少,然後按順序遍歷左二子和右兒子。這樣,如果更新到最優值,就能及時退出。這種算法在隨機數據上是lg的,但是在構造數據上約是sqrt的。
【BZOJ2716&2648】雙倍經驗。就是裸的K-D TREE模板套套。無壓力1A~。
【BZOJ3053】哎,說多了都是淚。這道題調了不知道多少時間。首先,它拓展到了K維空間上。這樣,cmp就只需判斷某一位的大小就行了。然後要查詢前m優值。因為m<=10,我為了效率,直接一遍做,開了一個數組記錄最優值。然後判斷最優值的時候裸O(n)(均攤)的更新答案。
對於那個估計函數也要稍稍改一下(因為是歐幾裡得距離),怎麼方便怎麼來!(反正只會影響到效率)
調了半天後,總算小數據對拍沒有問題了~~浪交!T了。。。
後來我估計在更新答案時速度太慢,於是一咬牙,把10個最優解開成了隊列......
然後大數據對拍~~什麼,秒WA?這下真的調了一個下午(因為我是剛學的),後來發現:RZZ的博客裡的nth過程用錯了。比如從l到r,中間是mid(默認數組下標從1開始),應該是a+l,a+mid,a+r+1。最後一個+1因為是虛指針。但是前面都不用+1的(上面的代碼已經修改過了)!!!!
最後又是RE。果斷要數據!——發現只有一個測試點。我先寫了個程序,把測試點拆成了好幾個。然後一測:全過!和在一起:RE!原來,l和r要及時清零!!!呵呵,多麼痛的領悟!
【截取程序】
var ss:string; cnt,n,m,a,i,j:longint; begin assign(input,'T.in'); reset(input); while (not(eof)) do begin inc(cnt); str(cnt,ss); ss:='T'+ss+'.in'; assign(output,ss); rewrite(output); readln(n,m); writeln(n,' ',m); for i:=1 to n do begin for j:=1 to m do begin read(a); write(a,' '); end; writeln; end; readln(n); writeln(n); for i:=1 to n do begin for j:=1 to m do begin read(a); write(a,' '); end; writeln; read(a); writeln(a); end; close(output); end; end.【對拍造數據】
#include#include #include using namespace std; int main() { freopen("T.in","w",stdout); srand((int)time(0)); int n=50000,m=4,i,j; printf("%d %d\n",n,m); for (i=1;i<=n;i++) { for (j=1;j<=m;j++) printf("%d ",rand()%10000+1); printf("\n"); } int Q=1000; printf("%d\n",Q); while (Q--) { for (i=1;i<=m;i++) printf("%d ",rand()%10000+1); printf("%d\n",rand()%5+1); } return 0; } 【AC代碼】
#include#include #include #define N 50005 #define INF 21390627567143.0 using namespace std; const int orzSYC=1; struct arr { int d[5],max[15],min[15],l,r,id; arr() {l=0;r=0;id=0;} }a[N*4],aa[N]; struct pop { double x;int id; friend bool operator < (const pop &a,const pop &b){return a.x ans; int n,m,Q,i,j,t,x[15],D,temp[21],root,opt,P,flag; inline int Read() { int x=0;char ch=getchar();bool positive=1; for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') positive=0; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return positive?x:-x; } inline int cmp(arr a,arr b) { return a.d[D] >1); nth_element(aa+l,aa+mid,aa+r+1,cmp); for (int i=0;i =a[k].d[deep]) swap(L,R); double now=sdis(k); if (L) ask(L,(deep+1)%m); int flag=0; if (ans.size()