bzoj4520 CQOI2016 K遠點對

題意:給定NN個點,求歐幾里得距離第KK大的點對的歐幾里得距離的平方。
數據範圍:1N1051\leq N \leq 10^51K1001\leq K \leq 100KN×(N1)2K\leq \frac{N\times (N-1)}{2}0Xi,Yi<2310\leq X_i,Y_i< 2^{31},時限3s3s

做法:網上大多都是隨機化,要麼是不太能給出複雜度證明的k-d樹。這裏我寫了一個確定性的做法,且有穩定的複雜度。
我們考慮這樣做,每次跑出一個凸包,然後在這個凸包上跑最遠點對,然後把最遠點對的一個點刪掉,繼續這樣跑,跑KK次,我們發現第KK遠的點對一定有一個點在這KK個點裏了,那我們拿一個堆維護,O(NK)O(NK)的掃一遍就可以得出答案了,我們的複雜度是O(NKlog(N))O(NKlog(N)),然後我們就得到了不知道幾分的好成績……
考慮優化這個東西,我們發現這個凸包每次只需要O(N)O(N),因爲我們可以從(1,1)(-1,-1)對這些點極角排序,然後跑兩邊凸包算法一拼就求出凸包了。然後關鍵就在於這個求最遠點對,這裏有一個算法能把它從Nlog(N)Nlog(N)優化到O(N)O(N)
這個算法利用了四邊形不等式的性質,四邊形不等式是一個矩陣,滿足對於任意一個2×22\times2的子矩陣,假設是
[abcd] \left[ \begin{matrix} a & b \\ c & d \\ \end{matrix} \right]
都有a+db+ca+d\geq b+c,這個性質可以推出對於任意一個子矩陣,左上角加右下角一定大於等於左下角加右上角,證明就是將自己包含的所有2×22\times 2的矩陣的a+dbca+d-b-c加起來,剛好就能得到這個結果。順便提一句,四邊形不等式其實更多在dp中起優化作用,類似的有決策單調性,在矩陣裏有完全單調性與之對應,滿足的是任意兩行兩列的交點若有左上小於右上,則有左下小於右下。這個有什麼用呢,因爲很明顯,滿足四邊形不等式的肯定滿足這個完全單調性。
我們發現這裏凸包的距離矩陣似乎也滿足這個性質,假設我們對於一個2×22\times 2的矩陣,左上角在(i,j)(i,j),則需要滿足的就是dis(pi,pj)+dis(pi+1,pj+1)dis(pi,pj+1)+dis(pj,pi+1)dis(p_i,p_j)+dis(p_{i+1},p_{j+1})\geq dis(p_i,p_{j+1})+dis(p_j,p_{i+1}),畫個圖就很直觀了:
在這裏插入圖片描述
這裏我們可以利用三角形兩邊之和大於第三邊,有:
AE+BEABAE+BE\leq ABCE+DECDCE+DE\leq CD
相加可得AE+BE+CE+DEAB+CDAE+BE+CE+DE\leq AB+CD
剛好就是上面這個式子,但是這樣就完了嗎,直覺告訴你並沒有,這裏有一個反例就是i=ji=j的時候,如圖:
在這裏插入圖片描述
其實這個也很好解決,我們可以把這個矩陣的主對角線一下的部分平移另一部分到右邊,剩下的地方用-\infin填充,如圖:
在這裏插入圖片描述
這個矩陣就滿足四邊形不等式了。
然後這裏有一個神仙算法把這個矩陣每一行的最大值用O(n+m)O(n+m)的時間複雜度求出,因爲我們有剛纔的結論,我們發現每行的最大值位置是單調上升的,這個可以用我們推出的完全單調性證明,因爲對於第ii行的最大值位置xx前面都滿足Ai,j<Ai,xA_{i,j}< A_{i,x},然後我們就發現Ai+1A_{i+1}肯定也有這些式子,所以易證這是單調的。
我們發現n×mn\times m的矩陣明顯不好,我們想個辦法把mm減到nn,這裏我們可以把矩陣變成這樣:我們找出一些行使得Ai,iAi,i+1A_{i,i}\geq A_{i,i+1},這樣我們就是一個n×nn\times n的矩陣了。
這裏舉個例子,比如矩陣
[1.2.53421248911109361113161514] \left[ \begin{matrix} 1.&2.&5&3&4&2 &1\\ 2&4&8&9&11&10&9\\ 3&6&11&13&16&15&14\\ \end{matrix} \right]
這個矩陣好像具備剛纔的性質,我們來消一下,首先我們選擇第一行的第一個和第二個位置,發現1<21<2,好第一行明顯沒用了,刪。
[2.5.34214891110961113161514] \left[ \begin{matrix} 2.&5.&3&4&2 &1\\ 4&8&9&11&10&9\\ 6&11&13&16&15&14\\ \end{matrix} \right]
然後我們繼續,發現2<52<5,刪
[5.3.42189111091113161514] \left[ \begin{matrix} 5.&3.&4&2 &1\\ 8&9&11&10&9\\ 11&13&16&15&14\\ \end{matrix} \right]
然後我們繼續,發現5>35>3了,55這一排可能是有用的,留着,進第二排。
[5342189.11.1091113161514] \left[ \begin{matrix} 5&3&4&2 &1\\ 8&9.&11.&10&9\\ 11&13&16&15&14\\ \end{matrix} \right]
這裏9<119< 11,這時候我們就把99這列刪了,然後我們發現前一列可能不滿足條件,於是退後一列
[5.4.2181110911161514] \left[ \begin{matrix} 5.&4.&2 &1\\ 8&11&10&9\\ 11&16&15&14\\ \end{matrix} \right]
發現5>45>4,這裏我們對55放心了,可以繼續往下做,注意這裏如果55比較小的話下場是和之前1,21,2下場一樣的。
下一步就是
[5421811.10.911161514] \left[ \begin{matrix} 5&4&2 &1\\ 8&11.&10.&9\\ 11&16&15&14\\ \end{matrix} \right]
發現11>1011>10,過
[5421811109111615.14.] \left[ \begin{matrix} 5&4&2 &1\\ 8&11&10&9\\ 11&16&15.&14.\\ \end{matrix} \right]
這裏15>1415>14,大沒有留1414之必要,刪掉,
[54281110111615] \left[ \begin{matrix} 5&4&2\\ 8&11&10\\ 11&16&15\\ \end{matrix} \right]
注意如果1515較小也需要後退一排,然後消出來可能不足nn列,找出一些補齊即可。
既然有上面這個就好辦多了,每次我們對n×nn\times n的,把奇數行拿出來一跑,最後一塊掃一遍得到偶數的答案。
時間複雜度的遞歸時:T(n)=T(n2)+O(n)T(n)=T(\frac{n}{2})+O(n),即O(n)O(n)
然後這題我們就做完了,算法常數較大但也是線性的。
時間複雜度:O(Nlog(N)+NKlog(K))O(Nlog(N)+NKlog(K)),瓶頸在最後的堆。
代碼又慢又臭又長:

#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=1e6+7;
const ll INF=9223372036854775806;
ll n,k,sz;
struct pt{ll x,y;}p[N],g[N];
ll s[N],dw[N],z[N];
bool is[N],e[N];
ll Sq(ll x){return x*x;}
ll dis(pt a,pt b){return Sq(a.x-b.x)+Sq(a.y-b.y);}
ll qdis(ll a,ll b){
 if(a==b)return 0;
 if(a>b||b>a+sz)return -INF;
 if(b>sz)b-=sz;
 return dis(g[a],g[b]);
}
bool cmp2(ll x,ll y){return x>y;}
bool cmp(pt a,pt b){return (b.x+1)*(a.y+1)<(b.y+1)*(1+a.x);}
bool cross(pt a,pt b,pt c){return (b.y-a.y)*(c.x-b.x)<=(b.x-a.x)*(c.y-b.y);}
vector<ll>v1[50],v2[50],d[50],w[50];
void work(ll x){
 if(v1[x].size()<3){
  rep0(i,v1[x].size()){
   ll mx=0,p;
   rep0(j,v2[x].size()){
    ll o=qdis(v1[x][i],v2[x][j]);
    if(o>mx){
     mx=o;
     p=j;
    }
   }
   d[x].push_back(p);
  }
  return;
 }
 w[x].clear();
 ll li=0,h=1;
 w[x].push_back(0);
 while(h<v2[x].size()){
  if(qdis(v1[x][li],v2[x][w[x][w[x].size()-1]])<qdis(v1[x][li],v2[x][h])){
   w[x].pop_back();
   if(li)li--;
   else{
    w[x].push_back(h);
    h++;
   }
  }
  else{
   if(li<v1[x].size()-1){
    w[x].push_back(h);
    li++;
   }
   h++;
  }
 }
 rep0(i,v2[x].size())e[i]=0;
 rep0(i,w[x].size())e[w[x][i]]=1;
 ll nw=v1[x].size()-w[x].size();
 w[x].clear();
 rep0(i,v2[x].size()){
  if(e[i])w[x].push_back(i);
  else if(nw)w[x].push_back(i),nw--;
 }
 v2[x+1].clear();
 v1[x+1].clear();
 rep0(i,w[x].size())v2[x+1].push_back(v2[x][w[x][i]]);
 rep0(i,v1[x].size())if(i&1)v1[x+1].push_back(v1[x][i]);
 work(x+1);
 d[x].clear();
 ll tp=0;
 rep0(i,v1[x].size()){
  if(i&1)tp=d[x+1][i/2],d[x].push_back(w[x][tp]);
  else{
   ll hp=(i==v1[x].size()-1)?v1[x].size()-1:d[x+1][i/2],mx=0,p;
   REP(j,tp,hp){
    ll o=qdis(v1[x][i],v2[x][w[x][j]]);
    if(o>mx)mx=o,p=j;
   }
   d[x].push_back(w[x][p]);
  }
 }
}
priority_queue<ll,vector<ll>,greater<ll> >Q;
int main(){
 scanf("%lld%lld",&n,&k);
 rep(i,n)scanf("%lld%lld",&p[i].x,&p[i].y);
 if(n<400){
  ll cnt=0;
  rep(i,n)rep(j,i-1)s[++cnt]=dis(p[j],p[i]);
  sort(s+1,s+cnt+1,cmp2);
  printf("%lld\n",s[k]);
  return 0;
 }
 sort(p+1,p+n+1,cmp);
 rep(u,k){
  ll tp=0,cnt=0;
  rep(i,n){
   if(is[i])continue;
   while(cnt>1&&cross(p[s[cnt-1]],p[s[cnt]],p[i]))cnt--;
   s[++cnt]=i;
  }
  rep(i,cnt)g[i]=p[s[i]],z[i]=s[i];
  for(ll i=s[cnt];i;i--){
   if(is[i])continue;
   while(tp>1&&cross(p[s[tp-1]],p[s[tp]],p[i]))tp--;
   s[++tp]=i;
  }
  REP(i,2,tp-1)g[++cnt]=p[s[i]],z[cnt]=s[i];
  sz=cnt;
  v1[1].clear();
  v2[1].clear();
  rep(i,sz)v1[1].push_back(i);
  rep(i,sz*2)v2[1].push_back(i);
  work(1);
  ll id,mx=0;
  rep(i,sz){
   ll o=qdis(i,v2[1][d[1][i-1]]);
   if(o>mx)mx=o,id=i;
  }
  is[z[id]]=1;
  dw[u]=z[id];
 }
 ll gg=0;
 memset(is,0,sizeof(is));
 rep(i,k){
  rep(j,n){
   if(is[j])continue;
   if(gg<k)Q.push(dis(p[dw[i]],p[j])),gg++;
   else{
    ll o=dis(p[dw[i]],p[j]);
    if(o>Q.top()){
     Q.push(o);
     Q.pop();
    }
   }
  }
  is[dw[i]]=1;
 }
 printf("%lld\n",Q.top());
 return 0;
}

我纔不會告訴你我是前幾天集訓的時候學的這個算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章