[Luogu4169] [Violet]天使玩偶/SJY摆棋子 [cdq分治/k-d tree]

博客探讨平面最近点对问题的解法。先考虑用cdq分治,将问题转化为三维偏序,把曼哈顿距离拆成四个方向处理,还可对cdq进行优化。又介绍k-d tree解法,分析其复杂度,包括建树、插入、删除等操作,还提及在不同情况下的应用及随机卡法。

[ L i n k \frak{Link} Link]


考虑用 cdq 分治解决?
显然依旧分成修改跟查询,考虑一维排序二维cdq?啊但是那显然不太对,cdq能解决三维偏序,
但是她不能解决最近点对啊。平面最近点对并不能这么干。
那我们考虑一点别哒。怎么把题目给出的问题转化成三维偏序?
“最近”点对就有一种偏序的感觉,但是怎么转化呢?
感觉很不好搞?
但是呢,但是呢,如果变成单纯的比较 x y 坐标大小,就很好做了呀?
因为是偏序问题,所以实际上也不好直接这么搞
比如说我们只考虑第三象限
如果简单考虑,对询问 ( x , y ) (x,y) (x,y) 直接求 x i ≤ x , y i ≤ y x_i\le x,y_i\le y xix,yiy 点中 min ⁡ ( x i − x + y i − y ) \min{(x_i-x+y_i-y)} min(xix+yiy)
这样对询问的依赖就比较大,
所以我们换一下思路,去掉 x x x y y y 那么
对询问 ( x , y ) (x,y) (x,y) 只需要求 x i ≤ x , y i ≤ y x_i\le x,y_i\le y xix,yiy 点中 max ⁡ ( x i + y i ) \max{(x_i+y_i)} max(xi+yi)
问题就被简化辣。
一维时间,二维横坐标,三维纵坐标


彳巴
那么只需要把曼哈顿距离拆开变成四个方向(四个象限)就不难搞了errrr
简单吧?(xxx
你可能会说,四个方向搞你马呢
这样,我们换一次方向就把整个图转一遍?
然后cdq的时候只处理在固定方向上的点。


cdq 的话,实际上还可以多加一个优化,就是把一开始的 n n n 个点提出来单独处理。
但是我没有写x


k k k-d tree 的话就是一个裸题了
复杂度:建树 best case O ( n log ⁡ n ) O(n\log n) O(nlogn) ,worst case O ( k n log ⁡ n ) O(kn\log n) O(knlogn)
空间复杂度 O ( 2 n ) O(2n) O(2n)
插入 O ( log ⁡ n ) O(\log n) O(logn) 删除 O ( log ⁡ n ) O(\log n) O(logn)
求最近点对 O ( n log ⁡ n ) O(n\log n) O(nlogn) in average ,worst case O ( n 2 ) O(n^2) O(n2)
数点 worst case O ( k n 1 − 1 / k ) O(kn^{1-1/k}) O(kn11/k)
高维空间中,一般 n ≫ 2 k n\gg2^k n2k 的时候用 k k k-d tree 找最/k近邻效果才会好一点;
实际上维度很高的时候,假如点数不多,求最近邻甚至不如枚举

那 cqoi 那道题跑 kdt 实际上就是玄学卡啦。(虽然的确就是要你 kdt 玄学卡
(不过那题随机化也可以大概大概,就是用那种转系的。。来分界求
虽然现在那种做法有点普及了,(可能?)容易被哈克
一般的最近/远邻也可以用各种骚操作随机卡,比如说
你可以预先确定一个松的下界,排序,某个坐标相差过小就可以去掉
暴枚然后不停更新
当然如果你觉得随机很不稳,又嫌得蛋疼 / 你觉得 kdt 比随机好写
那就大力 kdt 吧,记得多优化一下常数。。
至少基本的板子要足够优吧?(比如我下面那个板子就不是很好

至于这道题用 kdt 复杂度应该是 O ( n ) O(\sqrt{n}) O(n ) 的,因为是每次询问给出点的 k 近邻


cdq
#include<cstdio>
#include<cmath>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline void setmax(int&a,const int&b)
{
    if(a<b)a=b;
}
inline void setmin(int&a,const int&b)
{
    if(a>b)a=b;
}
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
int read()
{
    char ch = getchar(); bool w = 0; int x = 0;
    while (!isdigit(ch)) w |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
    return w ? -x : x;
}
const int MAXN = 600005;
const int INF = 0x3f3f3f3f;
int n, m, u;
struct pt
{
    int x;
    int y;
    int id;
    pt(int b=0, int c=0, int d=0)
    {
        x = b;
        y = c;
        id = d;
    }
    bool operator < (const pt& o) const
    {
        return x <= o.x;
    }
}o[MAXN], e[MAXN], sav[MAXN];
int x[MAXN], y[MAXN], tem[MAXN], met[MAXN], bit[1000005], ans[MAXN];
int p, q, tot, r, s;
void Modify(int x, int k)
{
    while(x<=s)
    {
        setmax(bit[x], k);
        x += x & -x;
    }
}
void Clear(int x)
{
    while (x<=s)
    {
        if(bit[x])bit[x]=0;
        else break;
        x += x & -x;
    }
}
int Query(int x)
{
    int ret = 0;
    while(x)
    {
        setmax(ret, bit[x]);
        x -= x & -x;
    }
    return ret;
}
int f;
void cdq(int L, int R)
{
    if (L == R) return;
    int Mid = L + R >> 1;
    cdq(L, Mid);
    cdq(Mid + 1, R);
    for (p = L, q = Mid + 1; q <= R; ++q)
    {
        if (o[q].id)
        {
            for (; p <= Mid && o[p].x <= o[q].x; ++p)
            {
                if (!o[p].id) Modify(o[p].y, o[p].x+o[p].y);
            }
            f = Query(o[q].y);
            if(f) setmin(ans[o[q].id],o[q].x+o[q].y-f);
        }
    }
    for (int i = L; i <= p; ++i) if (!o[i].id) Clear(o[i].y);
    merge(o+L, o+Mid+1, o+Mid+1, o+R+1, e+L);
    for (int i = L; i <= R; ++i) o[i] = e[i];
}
int rx, ry, rt;
void Del()
{
    rx=ry=rt=0;
    for (register int i=1;i<=m;++i)if(o[i].id)setmax(rx,o[i].x),setmax(ry,o[i].y);
    for (register int i=1;i<=m;++i)if(o[i].x<=rx&&o[i].y<=ry)e[++rt]=o[i];
    memcpy(o,e,sizeof(e));
}
int main()
{
    n = read(); m = read();
    
    register int *xwhere = x+1, *ywhere = y+1;
    for (register int i = 1; i <= n; ++i)
    {
        (*xwhere) = 1+read(), (*ywhere) = 1+read();
        setmax(r,*xwhere); setmax(s,*ywhere);
        o[i] = pt(*(xwhere++), *(ywhere++));
        
    }
    
    m += n;
    for (register int t, i = n + 1; i <= m; ++i)
    {
        t = read();
        (*xwhere) = 1+read(), (*ywhere) = 1+read();
        setmax(r,*xwhere); setmax(s,*ywhere);
        o[i] = pt(*(xwhere++), *(ywhere++), (t==2)?(++u):0);
    }
    
    ++r; ++s;
    memcpy(sav,o,sizeof(o));
    for (int i = 1; i <= u; ++i) ans[i] = INF;
    Del();cdq(1,rt);
    
    memcpy(o,sav,sizeof(sav));
    for (int i = 1; i <= m; ++i) o[i].x = r-o[i].x;
    Del();cdq(1,rt);
    
    memcpy(o,sav,sizeof(sav));
    for (int i = 1; i <= m; ++i) o[i].y = s-o[i].y;
    Del();cdq(1,rt);
    
    memcpy(o,sav,sizeof(sav));
    for (int i = 1; i <= m; ++i) o[i].x = r-o[i].x, o[i].y=s-o[i].y;
    Del();cdq(1,rt);
    
    for (int i = 1; i <= u; ++i) printf("%d\n", ans[i]);
    return 0;
}

kdt
把我调炸了。。。
#include<cstdio>
#include<cmath>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
int read()
{
    char ch = getchar(); int x = 0; bool w = 0;
    while (!isdigit(ch)) w |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
    return w ? -x : x;
}
#define max(DefVa,DefVb) ((DefVa)>(DefVb)?(DefVa):(DefVb))
#define min(DefVa,DefVb) ((DefVa)<(DefVb)?(DefVa):(DefVb))
#define setmax(DefVa,DefVb) ((DefVb)>(DefVa)?((DefVa)=(DefVb)):0)
#define setmin(DefVa,DefVb) ((DefVb)<(DefVa)?((DefVa)=(DefVb)):0)
#define abs(x) ((x)<0?(-(x)):(x))
const double Alph = 0.75;
const int INF = 0x3f3f3f3f;
const int MAXN = 1000005;
int n, m, Root, cd;
struct pt
{
    int x, y;
    pt(int xx=0, int yy=0)
    {
        x = xx;
        y = yy;
    }
}TemStack[MAXN], CorPt[MAXN];
int TemStackTot;
int Child[MAXN][2];
int Siz[MAXN];
int Xmn[MAXN], Xmx[MAXN], Ymn[MAXN], Ymx[MAXN];
int CorId[MAXN];
int Dim[MAXN];
int Rec[MAXN], top, Ntot;
#define NewNode() (top?Rec[top--]:++Ntot)
#define LChild(DefVal) Child[DefVal][0]
#define RChild(DefVal) Child[DefVal][1]
inline bool cmpx(const pt& a, const pt& b) { return a.x < b.x;}
inline bool cmpy(const pt& a, const pt& b) { return a.y < b.y;}
inline void Maintain(const int& x)
{
    static int l, r;
    l = LChild(x);
    r = RChild(x);
    
    Siz[x] = Siz[l] + Siz[r] + 1;
    Xmn[x] = Xmx[x] = CorPt[x].x;
    Ymn[x] = Ymx[x] = CorPt[x].y;
    
    if (l)
    {
        setmin(Xmn[x], Xmn[l]);
        setmin(Ymn[x], Ymn[l]);
        setmax(Xmx[x], Xmx[l]);
        setmax(Ymx[x], Ymx[l]);
    }
    
    if (r)
    {
        setmin(Xmn[x], Xmn[r]);
        setmin(Ymn[x], Ymn[r]);
        setmax(Xmx[x], Xmx[r]);
        setmax(Ymx[x], Ymx[r]);
    }
    
}
int Structure(int L, int R)
{
    if (L > R) return 0;
    
    if (L == R)
    {
    	CorId[L] = NewNode();
        Dim[CorId[L]] = 0;
        CorPt[CorId[L]] = TemStack[L];
        Maintain(CorId[L]);
        return CorId[L];
    }
    
    
    static int XMn, XMx, YMn, YMx;
    
    XMn = INF, XMx = 0, YMn = INF, YMx = 0;
    
    for (register int i = L; i <= R; ++i)
    {
        setmax(XMx, TemStack[i].x);
        setmin(XMn, TemStack[i].x);
        setmax(YMx, TemStack[i].y);
        setmin(YMn, TemStack[i].y);
    }
    
    
    int Mid = L + R >> 1;
    CorId[Mid] = NewNode();
    
    (XMx-XMn>=YMx-YMn)?
    (nth_element(TemStack + L, TemStack + Mid + 1, TemStack + R + 1, cmpx), Dim[CorId[Mid]] = 0):
    (nth_element(TemStack + L, TemStack + Mid + 1, TemStack + R + 1, cmpy), Dim[CorId[Mid]] = 1);
    
    CorPt[CorId[Mid]] = TemStack[Mid];
    
    LChild(CorId[Mid]) = Structure(L, Mid - 1);
    RChild(CorId[Mid]) = Structure(Mid + 1, R);
    
    Maintain(CorId[Mid]);
    return CorId[Mid];
    
}
int temN;
void Destroy(int x)
{
    if(LChild(x)) Destroy(LChild(x)), LChild(x) = 0;
    
    TemStack[++TemStackTot] = CorPt[x];
    CorId[TemStackTot] = x;
    Rec[++top] = x;
    
    if(RChild(x)) Destroy(RChild(x)), RChild(x) = 0;
}
void Insert(const int& x, const int& y)
{
    register int Pos = Root, Fa = 0;
    register bool Belong;
    
    while(Pos)
    {
        setmax(Xmx[Pos], x);
        setmin(Xmn[Pos], x);
        setmax(Ymx[Pos], y);
        setmin(Ymn[Pos], y);
        
        ++Siz[Pos];
        
        
        Belong = !(Dim[Pos]?(y<=CorPt[Pos].y):(x<=CorPt[Pos].x));
        
        if (!Child[Pos][Belong])
        {
            Child[Pos][Belong] = temN = NewNode();
            CorPt[temN] = pt(x,y); Maintain(temN); 
            return;
        }
        else if (Siz[Child[Pos][Belong]] + 1 > Siz[Pos] * Alph)
        {
            TemStackTot = 0; Destroy(Pos);
            TemStack[++TemStackTot] = pt(x,y);
            CorId[TemStackTot] = NewNode();
            if (Pos != Root) Child[Fa][Pos==RChild(Fa)] = Structure(1, TemStackTot), Maintain(Fa);
            else Root = Structure(1, TemStackTot);
            return;
        }
        else
        {
            Fa = Pos;
            Pos = Child[Pos][Belong];
        }
    }
}
inline int CalcDist(int p, int x, int y)
{
    return max(0,x-Xmx[p])+max(0,Xmn[p]-x)+max(0,y-Ymx[p])+max(0,Ymn[p]-y);
}
int Ans;
inline void Query(int Pos, const int& x, const int& y)
{
    setmin(Ans, abs(CorPt[Pos].x - x) + abs(CorPt[Pos].y - y));
    int DisL = LChild(Pos)?CalcDist(LChild(Pos), x, y):INF;
    int DisR = RChild(Pos)?CalcDist(RChild(Pos), x, y):INF;
    if(DisL < DisR)
    {
        if (DisL < Ans) Query(LChild(Pos), x, y);
        if (DisR < Ans) Query(RChild(Pos), x, y);
    }
    else
    {
        if (DisR < Ans) Query(RChild(Pos), x, y);
        if (DisL < Ans) Query(LChild(Pos), x, y);
    }
}
int main()
{
    n = read(), m = read();
    for (register int i = 1; i <= n; ++i) TemStack[i].x=read(), TemStack[i].y=read();
    
    TemStackTot = n;
    Root = Structure(1, n);
    TemStackTot = 0;
    
    for (register int t, x, y, i = 1; i <= m; ++i)
    {
        t=read(); x=read(); y=read();
        if (t&1) Insert(x, y);
        else Ans = INF, Query(Root, x, y), printf("%d\n", Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值