天天看點

bzoj 2648 SJY擺棋子

bzoj 2648 SJY擺棋子

  • 錢限題.題面可以看這裡.
  • 若對每個點都算一遍距離,顯然 \(T\) 爆,在此思想基礎上使用 \(kd-tree\) 配合估價函數來剪枝效果較好.
  • 具體來說,對于 \(kd-tree\) 上的一個節點,我們知道它這顆子樹所管轄的範圍,算出我們查詢的節點到這個範圍内可能的最小距離(下界),若這個距離都比目前記錄的答案劣或相等,再搜尋這顆子樹顯然沒有意義.
  • 這樣就完成了最優性剪枝.
  • 還有一個剪枝,若左子樹的距離下界小于右子樹的距離下界,則應先搜尋左子樹,否則先搜尋右子樹.
  • 感性了解一下,若一個兒子的距離下界小,先搜尋它,答案更有可能變得更小,如果小于或等于了另一個兒子的距離下界,就沒有必要再搜另一個了
  • 對于加點的操作,就和二叉搜尋樹一樣,從根節點往下,通過比較确定往哪邊走,走到适當的位置插入即可.疊代代替遞歸,常數較小,但要注意走的同時用新節點更新目前節點的資訊.
  • 插入過多時,由于 \(kd-tree\) 無法進行旋轉操作保持平衡,樹高可能會變得很大,使其退化成鍊,有三種方法處理.
    • 1.記錄一個平衡因子 \(\alpha\) ,像替罪羊樹那樣,及時拍扁重構.這樣寫最穩定,時間上(應該是)最優的.
    • 2.不及時重構,每加入一定量的節點後對整棵樹重構,這個量大概在 \(10^4\) 級别,可自行調整.這樣寫不太穩定,但實作非常簡單,隻多了幾行.
    • 3.離線處理,将所有加點操作都讀進來,一開始就全部建好,給每個點加個标記表示是否已經被插入(可用),真正插入時修改标記.這種寫法不太推薦,每種操作都要額外考慮标記問題,比較繁瑣,而且在資瓷離線的情況下,使用一些離線算法,編寫難度和時間效率都遠勝于 \(kd-tree\) .
\(kd-tree\) 最近點問題下大概是 \(O(玄學)\) , 處理矩形問題下最壞是 \(O(kn^{1-\frac 1 k})\) ???不會證明,有 \(dalao\) 得到了證明或證僞麻煩告知...
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
    int x=0;
    bool pos=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            pos=0;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return pos?x:-x;
}
const int MAXN=5e5+10;
int n,m,Dimen;
int rt,K=25000;
struct node{
    int v[2];
    int mi[2],ma[2];
    int ls,rs;
    bool operator < (const node &rhs) const
        {
            return v[Dimen]<rhs.v[Dimen]; 
        }
}Tree[MAXN<<1],A[MAXN<<1];
#define root Tree[o]
#define lson Tree[root.ls]
#define rson Tree[root.rs]
#define inf 1e9
void init()
{
    for(int i=0;i<2;++i)
        {
            Tree[0].mi[i]=inf;
            Tree[0].ma[i]=-inf;
        }
}
inline void pushup(int o)
{
    for(int i=0;i<2;++i)
        {
            root.mi[i]=min(root.mi[i],min(lson.mi[i],rson.mi[i]));
            root.ma[i]=max(root.ma[i],max(lson.ma[i],rson.ma[i]));
        }
}
int BuildTree(int l,int r,int dimen)
{
    Dimen=dimen;
    int mid=(l+r)>>1;
    int o=mid;
    nth_element(A+l,A+mid,A+r+1);
    for(int i=0;i<2;++i)
        {
            root.v[i]=A[mid].v[i];
            root.mi[i]=root.ma[i]=root.v[i];
        }
    root.ls=root.rs=0;
    if(l<=mid-1)
        root.ls=BuildTree(l,mid-1,dimen^1);
    if(mid+1<=r)
        root.rs=BuildTree(mid+1,r,dimen^1);
    pushup(o);
    return o;
}
node querynode;
int ans;//最近距離 
int estimate(int o)//估計o到querynode的距離 
{
    if(!o)
        return inf;
    int res=0;
    for(int i=0;i<2;++i)
        {
            if(querynode.v[i]<root.mi[i])
                res+=root.mi[i]-querynode.v[i];
            else if(querynode.v[i]>root.ma[i])
                res+=querynode.v[i]-root.ma[i];
        }
    return res;
}
void query(int o)
{
    int dist=abs(root.v[0]-querynode.v[0])+abs(root.v[1]-querynode.v[1]);//實際距離 
    ans=min(ans,dist);
    int dl=estimate(root.ls),dr=estimate(root.rs);
    if(dl<dr)
        {
            if(dl<ans)
                query(root.ls);
            if(dr<ans)
                query(root.rs);
        }
    else
        {
            if(dr<ans)
                query(root.rs);
            if(dl<ans)
                query(root.ls);
        }
}
void Insert()//像平衡樹一樣,比較大小向下走,走到合适的地方接上 
{
    ++n;
    for(int i=0;i<2;++i)
        {
            A[n].v[i]=Tree[n].v[i]=read();
            Tree[n].mi[i]=Tree[n].ma[i]=Tree[n].v[i];
        }
    for(int o=rt,dimen=0;o;dimen^=1)
        {
            for(int i=0;i<2;++i)
                {
                    root.mi[i]=min(root.mi[i],Tree[n].mi[i]);
                    root.ma[i]=max(root.ma[i],Tree[n].ma[i]);
                }
            if(Tree[n].v[dimen]<root.v[dimen])
                {
                    if(root.ls)
                        o=root.ls;
                    else
                        {
                            root.ls=n;
                            return;
                        }
                }
            else
                {
                    if(root.rs)
                        o=root.rs;
                    else
                        {
                            root.rs=n;
                            return;
                        }
                }
        }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i)
        for(int j=0;j<2;++j)
            A[i].v[j]=read();
    init();
    rt=BuildTree(1,n,0);
    while(m--)
        {
            int op=read();
            if(op==1)
                {
                    Insert();
                    if(n%K==0)
                        rt=BuildTree(1,n,0);
                }
            else
                {
                    querynode.v[0]=read();
                    querynode.v[1]=read();
                    ans=inf;
                    query(rt);
                    printf("%d\n",ans);
                }
        }
    return 0;
}                

轉載于:https://www.cnblogs.com/jklover/p/10406843.html