省选数据结构

Posted asd123www

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了省选数据结构相关的知识,希望对你有一定的参考价值。

省选数据结构:

好像将的都是一些科技。

一般都不会在板子上做什么修改啊。

大概列一下知识点:

字符串:后缀自动机,回文自动机。

有关于树的:LCT,KD-tree,可持久化数据结构,树套树(分治),点分治,替罪羊树。

其他一些:凸包,插头dp。

大部分都是很好理解的,个人感觉只有字符串的一些不是那么好理解。

其中有一些之前好像讲过,后面就不会在详细讲了。

 

一.可持久化数据结构

非常基础的知识点,用的非常多。

主席树大家都会了,可持久化01tire也一样,只说一下可持久化Treap。

似乎只有Treap能可持久化,反正我也没学splay

因为我们一次Merge,Split只会经过log个节点,直接新建就OK了。

这样的好处就是所有修改了的点我们都新建了,对于不同版本之间一点影响都没有。

让那些大部分用不到的点默默作出贡献。

HDU 6087:

题目大意:

一开始输入一个数组$A’$,然后copy到另一个$A$数组。

支持三种操作:

$1 l r$:求$[l,r]$内$A_{i}$的和

$2 l r$:for(int i=l;i<=r;i++) A[i]=A[i-k]

$3 l r$ : 对于$iepsilon [l,r],A_{i}=A‘_{i}$

分析:

主要是怎么维护操作2之后的序列。

一个显然的思路是倍增,Merge log次。

但是自己Merge自己会出现死循环的傻逼错误,直接可持久化就好了。

注意你的内存很大,一般是开一个比较大的数组没,在占用的点的个数达到一定限度之后rebuild。

技术分享图片
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 200500
#define LL long long 
using namespace std;
int n,m,a[N],tot,cnt;
#define tp pair<Treap*,Treap*>
struct Treap{
    Treap *ch[2];
    int size,val;
    LL sum;
    void pushup(){
        sum=ch[0]->sum+ch[1]->sum+val;
        size=ch[0]->size+ch[1]->size+1;
    }
}*null,*org,*root,mem[N<<5];
Treap * newTreap(int v){
    Treap *rt=mem+(++tot);
    rt->ch[0]=rt->ch[1]=null;
    rt->size=1;rt->val=rt->sum=v;
}
Treap * copy(Treap *x){
    Treap *rt=mem+(++tot);
    *rt=*x;return rt;
}
tp split(Treap *o,int k){
    if(o==null)return tp(null,null);
    Treap *rt=copy(o);
    tp x;
    if(o->ch[0]->size>=k){
        x=split(rt->ch[0],k);
        rt->ch[0]=x.second;
        rt->pushup();
        x.second=rt;
    }
    else{
        x=split(rt->ch[1],k-o->ch[0]->size-1);
        rt->ch[1]=x.first;
        rt->pushup();
        x.first=rt;
    }
    return x;
}
Treap * merge(Treap *a,Treap *b){
    if(a==null)return b;
    if(b==null)return a;
    if(rand()%(a->size+b->size)<a->size){
        Treap *rt=copy(a);
        rt->ch[1]=merge(rt->ch[1],b);
        rt->pushup();
        return rt;
    }
    else{
        Treap *rt=copy(b);
        rt->ch[0]=merge(a,rt->ch[0]);
        rt->pushup();
        return rt;
    }
}
void travel(Treap *rt){
    if(rt==null)return ;
    travel(rt->ch[0]);
    a[++cnt]=rt->val;
    travel(rt->ch[1]);
}
Treap * build(int l,int r){
    if(l>r)return null;
    int mid=(l+r)>>1;
    Treap *rt=newTreap(a[mid]);
    rt->ch[0]=build(l,mid-1);
    rt->ch[1]=build(mid+1,r);
    rt->pushup();
    return rt;
}
void rebuild(){
    tot=n;cnt=0;
    travel(root);
    root=build(1,n);
}
LL query(int x){
    Treap *now=root;
    LL ans=0;
    while(1){
        if(now==null)return ans;
        if(now->ch[0]->size>=x)now=now->ch[0];
        else ans+=now->ch[0]->sum+now->val,x-=now->ch[0]->size+1,now=now->ch[1];
    }
}
Treap * qp (Treap *a,int b){
    Treap *c=null;
    for(;b;b>>=1,a=merge(a,a))
        if(b&1)c=merge(c,a);
    return c;
}
void work(int l,int r){
    int k;scanf("%d",&k);
    tp x=split(root,l-1);
    tp y=split(x.second,r-l+1);
    tp z=split(x.first,l-k-1);
    tp w=split(z.second,(r-l+1)%k);
    Treap *rt=qp(z.second,(r-l+1)/k);
    rt=merge(rt,w.first);
    root=merge(merge(x.first,rt),y.second);
    if((N<<5)-tot<1000)rebuild();
}
void update(int l,int r){
    tp x=split(root,l-1);
    tp y=split(x.second,r-l+1);
    tp z=split(org,l-1);
    tp w=split(z.second,r-l+1);
    root=merge(merge(x.first,w.first),y.second);
    if((N<<5)-tot<1000)rebuild();
}
int main(){
    null=mem;
    null->ch[0]=null->ch[1]=null;
    null->size=null->val=null->sum=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    root=org=build(1,n);
    int o,l,r;
    while(m--){
        scanf("%d%d%d",&o,&l,&r);
        if(o==1)printf("%lld
",query(r)-query(l-1));
        if(o==2)work(l,r);
        if(o==3)update(l,r);
    }
    return 0;
}
任一凡的code
技术分享图片
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;
#define RG register
#define LL long long
#define N 200010
char B[1<<15],*S=B,*T=B;
#define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
inline int read()
{
    RG int x=0;RG char c=getc;
    while(c<0|c>9)c=getc;
    while(c>=0&c<=9)x=10*x+(c^48),c=getc;
    return x;
}
struct node
{
    node *ch[2];
    int val,size;LL sum;
    inline void update()
    {
        size=1+ch[0]->size+ch[1]->size;
        sum=val+ch[0]->sum+ch[1]->sum;
    }
}*null,*net,*root,mem[N*7];
int n,m,a[N],b[N],tot,cnt,opt;
inline node* newnode(int val)
{
    node *o=mem+(tot++);
    o->val=o->sum=val;o->size=1;
    o->ch[0]=o->ch[1]=null;return o;
}
inline node* cop(node *a)
{
    if(a==null)return null;
    node *o=mem+(tot++);*o=*a;
    return o;
}
inline node* merge(node *a,node *b)
{
    if(a==null)return b;
    if(b==null)return a;
    if(rand()%(a->size+b->size) < a->size)
        {node *o=cop(a);o->ch[1]=merge(o->ch[1],b),o->update();return o;}
    node *o=cop(b);o->ch[0]=merge(a,o->ch[0]),o->update();return o;
}
#define D pair<node*,node*>
inline D split(node *o,int k)
{
    if(o==null)return D(null,null);
    node *ret=cop(o);D y;
    if(o->ch[0]->size >=k)y=split(ret->ch[0],k),ret->ch[0]=y.second,ret->update(),y.second=ret;
    else y=split(ret->ch[1],k-o->ch[0]->size-1),ret->ch[1]=y.first,ret->update(),y.first=ret;
    return y;
}
inline void recycle(node *o)
{
    if(o==null)return;
    recycle(o->ch[0]);b[++cnt]=o->val;recycle(o->ch[1]);
}
inline node* build(int l,int r)
{
    if(l>r)return null;
    RG int mi=l+r>>1;
    node *o=newnode(opt?b[mi]:a[mi]);
    o->ch[0]=build(l,mi-1),o->ch[1]=build(mi+1,r);
    o->update();return o;
}
inline void rebuild()
{
    tot=n;cnt=0;recycle(root);
    //opt=0;net=build(1,n);
    opt=1;root=build(1,n);
}
inline LL query(node *o,int k)
{
    if(o==null)return 0;
    return o->ch[0]->size >= k ? query(o->ch[0],k) : o->ch[0]->sum + o->val + query(o->ch[1],k-o->ch[0]->size-1);
}
inline void print(node *o)
{
    if(o==null)return;
    print(o->ch[0]),printf("%d ",o->val);print(o->ch[1]);
}
inline node* pow(node *o,int mi)
{
    node *ret=null;
    for(;mi;mi>>=1,o=merge(o,o))
        if(mi&1)ret=merge(ret,o);
    return ret;
}
inline void work(int l,int r,int L,int R,int len)
{
    D x=split(root,r);
    D y=split(x.second,R-L+1);
    D z=split(x.first,l-1);
    D c=split(z.second,(R-L+1)%len);
    node *o=pow(z.second,(R-L+1)/len);
    o=merge(o,c.first);
    root=merge(merge(x.first,o),y.second);
    if( 7*N-tot < 1000)rebuild();
}
inline void recover(int l,int r)
{
    D x=split(root,l-1);
    D y=split(x.second,r-l+1);
    D z=split(net,l-1);
    D c=split(z.second,r-l+1);
    root=merge(merge(x.first,c.first),y.second);
    if( 7*N-tot < 1000)rebuild();
}
int main()
{
    n=read();
    RG int i,m=read(),l,r,k;
    for(i=1;i<=n;++i)a[i]=read();
    null=new node();null->ch[0]=null->ch[1]=null;
    null->size=null->val=null->sum=0;
    root=net=build(1,n);
    for(i=1;i<=m;++i)
        switch(read())
        {
            case 1:l=read(),r=read();printf("%lld
", query(root,r)-( (l>1)?query(root,l-1):0 ) );break;
            case 2:l=read(),r=read();k=read();work(l-k,l-1,l,r,k);break;
            case 3:l=read(),r=read();recover(l,r);break;
        }
}
周自恒的code
技术分享图片
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define for1(a,b,i) for(register int i=a;i<=b;++i)
#define FOR2(a,b,i) for(register int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read () {
    char x=getchar();
    int f=1,sum=0;
    for(;(x<0||x>9);x=getchar()) if(x==-) f=-1;
    for(;x>=0&&x<=9;x=getchar()) sum=sum*10+x-0;
    return f*sum;
}
  
#define N 10000000
#define M 10000005
#define maxn 200005
int node_num;
int n,m,val_[2][maxn];
struct SP {
    SP *ch[2];
    int size,v;
    ll sum;
      
    inline void rain () {
        sum=ch[0]->sum+ch[1]->sum+v;
        size=ch[0]->size+ch[1]->size+1;
    }
}*null,*g,*root,pool_[M];
  
typedef pair<SP*,SP*> D;
  
inline void clear () {
    node_num=0;
    for1(0,N,i) {
        pool_[i].ch[0]=pool_[i].ch[1]=null;
        pool_[i].sum=pool_[i].size=pool_[i].v=0;
    }
}
  
inline void copy_ (SP *&o,SP *a) {
    o->ch[0]=a->ch[0];
    o->ch[1]=a->ch[1];
    o->v=a->v,o->size=a->size,o->sum=a->sum;
}
  
inline D split(SP *a,int k) {
    if(a==null) return D(null,null);
    D y;
    int num=a->ch[0]->size;
    SP *o=&pool_[node_num++];
    copy_(o,a);
    if(num>=k) {
        y=split(o->ch[0],k);
        o->ch[0]=y.second;
        o->rain();
        y.second=o;
    }
    else {
        y=split(o->ch[1],k-num-1);
        o->ch[1]=y.first;
        o->rain();
        y.first=o;
    }
    return y;
}
  
inline SP* Merge(SP *a,SP *b) {
    SP *o=&pool_[node_num++];
    if(b==null) return o=a;
    if(a==null) return o=b;
    if(rand()%(a->size+b->size+1)+1<=a->size) {
        copy_(o,a);
        o->ch[1]=Merge(a->ch[1],b);
    }
    else {
        copy_(o,b);
        o->ch[0]=Merge(a,b->ch[0]);
    }
    o->rain();
    return o;
}
  
inline SP* build (int *a,int l,int r) {
    if(l>r) return null;
    int mid=l+r>>1;
    SP *o=&pool_[node_num++];
    o->v=a[mid];
    o->sum=o->size=0;
    o->ch[0]=build(a,l,mid-1);
    o->ch[1]=build(a,mid+1,r);
    o->rain();
    return o;
}
  
inline void dfs(SP *o,int k) {
    if(o==null) return;
    dfs(o->ch[0],k);
    val_[1][k+o->ch[0]->size+1]=o->v;
    dfs(o->ch[1],k+o->ch[0]->size+1);
}
  
inline void init() {
    dfs(root,0);
    clear();
    g=build(val_[0],1,n);
    root=build(val_[1],1,n);
}
  
int main () {
//freopen("a.in","r",stdin);
//freopen("zj.out","w",stdout);
    null=new SP();
    null->ch[0]=null->ch[1]=null;
    null->size=null->sum=null->v=0;
    g=root=null;
      
    n=read(),m=read();
    for1(1,n,i) val_[0][i]=val_[1][i]=read();
    init();
      
    while (m--) {
        int opt=read();
        int l=read(),r=read(),k;
        if(opt==1) {
            D y=split(root,l-1);
            D z=split(y.second,r-l+1);
            printf("%lld
",z.first->sum);
        }
        else if(opt==2) {
            k=read();
            int len=(r-l+1)%k;
              
            if(r-l+1>=k) {
                int c=(r-l+1)/k;
                D y=split(root,l-k-1);
                D z=split(y.second,k);
                  
                D t1=split(root,l-1);
                D t2=split(t1.second,r-l+1-len);
                  
                root=t1.first;
                  
                SP *o=z.first;
                for(;c;c>>=1,o=Merge(o,o)) 
                    if(c&1) root=Merge(root,o);
                root=Merge(root,t2.second);
            }
            if(len) {
                D y=split(root,l-k-1);
                D z=split(y.second,len);
                  
                D t1=split(root,r-len);
                D t2=split(t1.second,len);
                  
                root=Merge(t1.first,Merge(z.first,t2.second));
            }
        }
        else {
            D y=split(g,l-1);
            D z=split(y.second,r-l+1);
              
            D t1=split(root,l-1);
            D t2=split(t1.second,r-l+1);
              
            root=Merge(t1.first,Merge(z.first,t2.second));
        }
        if(node_num+1000>N) init();
    }
}
我的code

我的代码长而且慢,你们参考前两个代码。

 

二.树套树(分治)

这个东西用的也很多。

其实就是对于每个外层线段树节点看作是一棵线段树。

每次操作会搞经过$logn$个外层节点,每个外层节点的线段树你又会走$logn$个。

所以就是$log^2n$的复杂度了,内存是一个$logn$的,但是如果你搞得是主席树套线段树就是$log^2n$了。

不过你也可以搞线段树+平衡树,根据不同需求自己选择吧。

你也可以写树套树套树娱乐一下,我原来写了一个,$nleq 5e4$。

不过一定是自己傻逼,因为不会有正解是这个。

 

分治的话大概就是将一层线段树搞成了分治,另一层用个线段树树状数组啥的维护。

技术分享图片
inline void solve(int l,int r) {
    if(l==r) return;
    int mid=l+r>>1;
    solve(l,mid);
    /*
    操作
    */
    solve(mid+1,r);
}
inline void solve(int l,int r) {
    if(l==r) return;
    int mid=l+r>>1;
    solve(l,mid),solve(mid+1,r);
    /*
    操作
    */
}
分治的一般形式

你可以认为solve(l,r)之后这个区间就完全被弄好了。

复杂度也是$log^2n$的,但是代码量就少多了。

不过可惜的是这个东西只能搞离线的。

bzoj 3295:

就是给你一个序列,然后每次删掉一个数,并动态查询这个序列的逆序对个数。

树套树:删掉一个点就是去掉它贡献的逆序对,直接线段树套平衡树就OK了。

分治:维护类似树套树每次减去的值,即这次删掉的点和其他没有删掉的点形成的逆序对。

按照删去时间排序,那么统计后删去的点和第i次删去的点形成的逆序对个数,直接树状数组就好了。

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<algorithm>
#define M 200005
#define for1(a,b,i) for(register int i=a;i<=b;++i)
#define for2(a,b,i) for(register int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read(){
    char x=getchar(); int f=1,sum=0;
    for(;!isdigit(x);x=getchar()) if(x==-) f=-1;
    for(;isdigit(x);x=getchar()) sum=sum*10+x-0;
    return f*sum;
}
 
int n,m,to[M],ans[M],OO[M],shu[M];
struct node{int a,b,c,id;bool J;}a[M],dui[M];
bool comp1(node a,node b){return a.a<b.a;}
bool comp2(node a,node b){return a.b<b.b;}
inline void T_add(int x){while(x<=n) ++shu[x],x+=x&-x;}
inline int query(int x){int sum=0;while(x) sum+=shu[x],x-=x&-x;return sum;}
 
void CDQ(int l,int r){
    int mid=l+r>>1,tot_=0; if(l==r) return;
    for1(l,mid,i) dui[++tot_]=a[i];
    for1(mid+1,r,i) dui[++tot_]=a[i],dui[tot_].J=1,ans[a[i].id]-=query(a[i].c-1);
    sort(dui+1,dui+tot_+1,comp2);
    for1(1,tot_,i)
       if(dui[i].J) ans[dui[i].id]+=query(dui[i].c-1);
       else T_add(dui[i].c);
    CDQ(l,mid),CDQ(mid+1,r);
}
 
int main(){
//freopen("inverse.in","r",stdin);
//freopen("inverse.out","w",stdout);
    n=read(),m=read();  ll haha=0;
    for1(1,n,i) a[i].c=read(),a[i].b=i,to[a[i].c]=i,a[i].id=i;
    for1(1,n,i) haha+=i-1-query(a[i].c),T_add(a[i].c); memset(shu,0,sizeof(shu));
    for1(1,m,i){
       int x=read();  OO[i]=x;
       a[to[x]].a=m-i+2;
    }
    for1(1,n,i) if(!a[i].a) a[i].a=1;
     
    for1(1,n,i) a[i].c=n-a[i].c+1;
    sort(a+1,a+n+1,comp1);CDQ(1,n);
    memset(shu,0,sizeof(shu));
    for1(1,n,i) a[i].c=n-a[i].c+1;
    for1(1,n,i) a[i].b=n-a[i].b+1;
    sort(a+1,a+n+1,comp1);CDQ(1,n);
    for1(1,m,i) printf("%lld
",haha),haha-=ans[to[OO[i]]];
}
分治
技术分享图片
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<ctime>
#define M 100005
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define for2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read(){
    char x=getchar(); int f=1,sum=0;
    for(;!isdigit(x);x=getchar()) if(x==-) f=-1;
    for(;isdigit(x);x=getchar()) sum=sum*10+x-0;
    return f*sum;
}
 
struct Tree{
    struct Treap{
       Treap *ch[2];
       int size,v,rnd;
       void rain(){size=(ch[1]?ch[1]->size:0)+(ch[0]?ch[0]->size:0)+1;}
       Treap(int x){size=1,v=x,rnd=rand(),ch[0]=ch[1]=NULL;}
    }*root;
    typedef pair<Treap*,Treap*> D;
    inline int size(Treap *o){return o?o->size:0;}
    Treap *Merge(Treap *a,Treap *b){
       if(!a) return b; if(!b) return a;
       if(a->rnd < b->rnd)
          {a->ch[1]=Merge(a->ch[1],b),a->rain();return a;}
       else
          {b->ch[0]=Merge(a,b->ch[0]),b->rain();return b;}
    }
    D split(Treap *o,int kth){
       if(!o) return D(NULL,NULL);
       D y;  int num_=size(o->ch[0]);
       if(num_>=kth)
         {y=split(o->ch[0],kth),o->ch[0]=y.second,o->rain(),y.second=o;}
       else
         {y=split(o->ch[1],kth-num_-1),o->ch[1]=y.first,o->rain(),y.first=o;}
       return y; 
    }
    int find(Treap *o,int x){
       if(o==NULL) return 0;
       if(o->v >= x) return find(o->ch[0],x);
       else return find(o->ch[1],x)+size(o->ch[0])+1;
    }
    inline void insert(int x){
       int kth=find(root,x);
       D y=split(root,kth);
       Treap *oo=new Treap(x);
       root=Merge(y.first,Merge(oo,y.second));
    }
    inline void erase(int x){
       int kth=find(root,x);
       D y=split(root,kth);
       D z=split(y.second,1);
       root=Merge(y.first,z.second); 
    }
}YY[M];
int n,m,a[M],to[M];  ll ans;
namespace HH{
    int shu[M];
    inline void add(int x){while(x<=n) ++shu[x],x+=x&-x;}
    inline int query(int x){
       int sum=0;while(x) sum+=shu[x],x-=x&-x;
       return sum;
    }
    ll getshu(){
       ll num_=0;
       for1(1,n,i){
          num_+=i-1-query(a[i]);
          add(a[i]); 
       }
       return num_;
    }
}
 
inline void add(int x,int zhi,bool J){
    if(J) while(x<=n){YY[x].insert(zhi),x+=x&-x;}
    else  while(x<=n){YY[x].erase(zhi),x+=x&-x;}
}
inline int query(int x,int zhi,bool J){
    int num=0;
    if(J) 
       while(x){
          int tot=YY[x].root?YY[x].root->size:0;
          num+=tot-YY[x].find(YY[x].root,zhi);
          x-=x&-x;
       }
    else
       while(x){
          num+=YY[x].find(YY[x].root,zhi);
          x-=x&-x;
       }
    return num;
}
 
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
    n=read(),m=read();
    for1(1,n,i) a[i]=read(),to[a[i]]=i;
    for1(1,n,i) add(i,a[i],1);
    ans=HH::getshu();
    while(m--){
       int x=read();
       printf("%lld
",ans);
       if(to[x]!=1) ans-=query(to[x]-1,x,1);
       if(to[x]!=n) ans-=query(n,x,0)-query(to[x],x,0);
       add(to[x],x,0);
    }
//cout<<(double)clock()/CLOCKS_PER_SEC<<endl;
}
树套树

注意我分治的时候每次sort了,这样会很慢,大家可以去网上找一下不sort的代码,但是会比较长。

注意你通常不用打solve1(),solve2(),solve3()...因为你通常可以在一个里面写多个。

 

三.KD-tree+替罪羊树。

KD-tree就是一个可以维护求多维查找的一个东西。

一次询问复杂度是$n^{1/2}$,不过我不会证明。

而且我只做过维护两维的,据说维数越多复杂度越接近$n^2$。

我们可以维护$|x_{i}-ax|+|y_{i}-ay|$的最值,$a*x_{i}+b*y_{i}$的最值。

以及维护二维平面上一些图形的相交情况等等,这个东西还是挺有用的。

 

bzoj 1941:

技术分享图片
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#define for1(a,b,i) for(register int i=a;i<=b;++i)
#define FOR2(a,b,i) for(register int i=a;i>=b;--i)
#define M 500005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define inf 1000000000 
using namespace std;
typedef long long ll;
inline int read(){
    char x=getchar();int f=1,sum=0;
    for(;!isdigit(x);x=getchar()) if(x==-) f=-1;
    for(;isdigit(x);x=getchar()) sum=sum*10+x-0;
    return f*sum; 
}
   
int n,root,now_K,MAX,MIN;
struct point{int mn[2],mx[2],d[2],l,r;}tr[M];
   
inline int get_D(int a1,int b1,int a2,int b2){
    return abs(a1-a2)+abs(b1-b2);
}
   
inline int get_min(int x,int y,int now){
    int d0,d1;
    d0=max(max(0,tr[now].mn[0]-x),max(0,x-tr[now].mx[0]));
    d1=max(max(0,tr[now].mn[1]-y),max(0,y-tr[now].mx[1]));
    return d0+d1;
}
inline int get_max(int x,int y,int now){
    int d0,d1;
    d0=max(abs(x-tr[now].mn[0]),abs(tr[now].mx[0]-x));
    d1=max(abs(y-tr[now].mn[1]),abs(tr[now].mx[1]-y));
    return d0+d1;
}
   
bool comp(point a,point b){
    return a.d[now_K]<b.d[now_K]||(a.d[now_K]==b.d[now_K]&&a.d[now_K^1]<b.d[now_K^1]);
}
   
inline void update(int v,int u){
    for1(0,1,i)
       tr[v].mx[i]=max(tr[v].mx[i],tr[u].mx[i]),
       tr[v].mn[i]=min(tr[v].mn[i],tr[u].mn[i]);
}
   
inline int build(int l,int r,int o){
    int mid=l+r>>1; now_K=o;
    nth_element(tr+l,tr+mid,tr+r+1,comp);
    for1(0,1,i) tr[mid].mn[i]=tr[mid].mx[i]=tr[mid].d[i];
    if(mid-1>=l) tr[mid].l=build(l,mid-1,o^1),update(mid,tr[mid].l);
    if(r>=mid+1) tr[mid].r=build(mid+1,r,o^1),update(mid,tr[mid].r);
    return mid;
}
   
inline void query_min(int now,int x,int y){
    int d1=inf,d2=inf;
    if(get_D(x,y,tr[now].d[0],tr[now].d[1]))
        MIN=min(MIN,get_D(x,y,tr[now].d[0],tr[now].d[1]));
    if(tr[now].l) d1=get_min(x,y,tr[now].l);
    if(tr[now].r) d2=get_min(x,y,tr[now].r);
    if(d1<d2){
        if(d1<MIN) query_min(tr[now].l,x,y);
        if(d2<MIN) query_min(tr[now].r,x,y);
    }
    if(d1>=d2){
        if(d2<MIN) query_min(tr[now].r,x,y);
        if(d1<MIN) query_min(tr[now].l,x,y);
    }
}
   
inline void query_max(int now,int x,int y){
    int d1=0,d2=0;
    MAX=max(MAX,get_D(x,y,tr[now].d[0],tr[now].d[1]));
    if(tr[now].l) d1=get_max(x,y,tr[now].l);
    if(tr[now].r) d2=get_max(x,y,tr[now].r);
    if(d1>d2){
        if(d1>MAX) query_max(tr[now].l,x,y);
        if(d2>MAX) query_max(tr[now].r,x,y);
    }
    if(d1<=d2){
        if(d2>MAX) query_max(tr[now].r,x,y);
        if(d1>MAX) query_max(tr[now].l,x,y);
    }
}
   
inline int QUERY_MAX(int id){
    MAX=0;
    query_max(root,tr[id].d[0],tr[id].d[1]);
    return MAX;
}
 
inline int QUERY_MIN(int id){
    MIN=inf;
    query_min(root,tr[id].d[0],tr[id].d[1]);
    return MIN;
}
   
int main(){
//freopen("a.in","r",stdin);
    n=read();
    for1(1,n,i) tr[i].d[0]=read(),tr[i].d[1]=read();
    root=build(1,n,0);
    int ans=inf;
    for1(1,n,i) {
        int num=QUERY_MAX(i)-QUERY_MIN(i);
        ans=min(ans,num);
    }
    cout<<ans<<endl;
}
KD-tree模板

 

有时我们不能一次build好整个KD-tree,或者我们要支持删除。

这时就需要利用替罪羊树rebuild的思路了,在插入或删除之后重构掉不平衡的子树。

还有时会让我们查询K大,直接搞一个堆每次暴力找就行了。。。

技术分享图片
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <queue>
#define for1(a,b,i) for(register int i=a;i<=b;++i)
#define FOR2(a,b,i) for(register int i=a;i>=b;--i)
#define M 100005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define inf 1000000001
using namespace std;
typedef long long ll;
inline int read(){
    char x=getchar();int f=1,sum=0;
    for(;!isdigit(x);x=getchar()) if(x==-) f=-1;
    for(;isdigit(x);x=getchar()) sum=sum*10+x-0;
    return f*sum; 
}
 
int n,K,CMP,ax[2];
priority_queue<ll>cc;
struct node{
    int x[2];
    bool operator <(const node &a)const{
       return x[CMP]==a.x[CMP]?x[CMP^1]<a.x[CMP^1]:x[CMP]<a.x[CMP];
    }
}a[M];
struct SP{
    SP*ch[2];
    int mn[2],mx[2],x[2];
    SP(){}
    inline void rain(){
        for1(0,1,i)
           mn[i]=min(ch[0]->mn[i],ch[1]->mn[i]),
           mx[i]=max(ch[0]->mx[i],ch[1]->mx[i]),
           mn[i]=min(mn[i],x[i]),mx[i]=max(mx[i],x[i]);
    }
}*null,*root;
 
inline SP* build(int l,int r,int k){
    if(l>r) return null;
    int mid=l+r>>1; CMP=k;
    nth_element(a+l,a+mid,a+r+1);
    SP* o=new SP();
    for1(0,1,i) o->x[i]=a[mid].x[i];
    o->ch[0]=build(l,mid-1,k^1);
    o->ch[1]=build(mid+1,r,k^1);
    o->rain();
    return o;
}
 
inline ll get_max(SP*o){
    ll dis=0;int x;
    for1(0,1,i){
        x=max(o->mx[i]-ax[i],ax[i]-o->mn[i]);
        dis+=1ll*x*x;
    }
    return dis;
}
 
inline void query(SP*o){
    if(o==null) return;
    ll MAX=get_max(o);
    if(cc.size()==K&&(-cc.top())>MAX) return;
    ll dis=0; int x;
    for1(0,1,i) x=ax[i]-o->x[i],dis+=1ll*x*x;
    cc.push(-dis);
    if(cc.size()>K) cc.pop();
    ll tmp1=get_max(o->ch[0]),tmp2=get_max(o->ch[1]);
    if(tmp1>tmp2) query(o->ch[0]),query(o->ch[1]);
    else          query(o->ch[1]),query(o->ch[0]);
}
 
int main(){
//freopen("a.in","r",stdin);
    null=new SP(); null->ch[0]=null->ch[1]=null;
    for1(0,1,i) null->mn[i]=inf,null->mx[i]=-inf;
    root=null;
    n=read(),K=read();  K*=2;
    for1(1,n,i) a[i].x[0]=read(),a[i].x[1]=read();
    root=build(1,n,0);
    for1(1,n,i){
       ax[0]=a[i].x[0],ax[1]=a[i].x[1];
       query(root);
    }
    printf("%lld",-cc.top());
//cout<<(double)clock()/CLOCKS_PER_SEC<<endl;
}
求K大
技术分享图片
struct node{
    node *ch[2];
    int mn[2],mx[2],a[2],size,k;
    node(){}
    void rain(){
        for1(0,1,i)
           mn[i]=min(ch[0]->mn[i],ch[1]->mn[i]),
           mx[i]=max(ch[0]->mx[i],ch[1]->mx[i]),
           mn[i]=min(mn[i],a[i]),mx[i]=max(mx[i],a[i]);
        size=ch[0]->size+ch[1]->size+1;
    }
}*root[N*4],*dui[N*4],*null;
 
inline bool comp(node *x,node *y)
{return x->a[CMP]==y->a[CMP]?x->a[CMP^1]<y->a[CMP^1]:x->a[CMP]<y->a[CMP];}
   
inline node *get_node(int KK){
    node *oo= new node();
    oo->ch[0]=oo->ch[1]=null;
    for1(0,1,i) oo->mn[i]=oo->mx[i]=oo->a[i]=A[i];
    oo->size=1; oo->k=KK; 
    return oo;
}
   
inline node** T_add(node*&now,int k){
    if(now==null){
        now=get_node(k);
        return &null;
    }
    node **p;
    if(A[k]<now->a[k]||(A[k]==now->a[k]&&A[k^1]<now->a[k^1]))
        p=T_add(now->ch[0],k^1);
    else p=T_add(now->ch[1],k^1);
    now->rain();
    if(max(now->ch[0]->size,now->ch[1]->size)>now->size*alpa+5)
        return &now;
    return p;
}
   
inline void dfs(node *now){
    if(now==null) return;
    dfs(now->ch[0]);
    dui[++cnt]=now;
    dfs(now->ch[1]);
}
   
inline node *rebuild(int l,int r,int k){
    if(l>r) return null;
    int mid=l+r>>1;  CMP=k;
    nth_element(dui+l,dui+mid,dui+r+1,comp);
    dui[mid]->ch[0]=rebuild(l,mid-1,k^1);
    dui[mid]->ch[1]=rebuild(mid+1,r,k^1);
    dui[mid]->k=k,dui[mid]->rain();
    return dui[mid];
}
   
inline void INSERT(int be){
    node ** p=T_add(root[be],0);
    if((*p)!=null){
        cnt=0; 
        dfs(*p);
        *p=rebuild(1,cnt,(*p)->k);
    }
}
替罪羊重构

如果大家想锻炼一下代码能力的话可以写一下替罪羊树的三个题,不过一定不要一直让别人帮忙调代码。

 

三.LCT

 

以上是关于省选数据结构的主要内容,如果未能解决你的问题,请参考以下文章

省选必知

关于省选

省选数据结构

省选退役记

省选前模板复习

[转]省选算法汇总