省选数据结构
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; }
#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; } }
#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(); } }
我的代码长而且慢,你们参考前两个代码。
二.树套树(分治)
这个东西用的也很多。
其实就是对于每个外层线段树节点看作是一棵线段树。
每次操作会搞经过$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; }
有时我们不能一次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; }
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
以上是关于省选数据结构的主要内容,如果未能解决你的问题,请参考以下文章