K-D树
一般用来解决各种二维平面的点对统计,也许一般非正解?
没时间慢慢写了,打完这个赛季后补细节
(绝赞咕咕中)
建树板子(2020.2.20更新):
const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int p[DIM]; int lb[DIM],rb[DIM]; Node(int x=0,int y=0,int z=0) { p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; //创建新节点 void newnode(int x) { ls[x]=rs[x]=0; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } //用子节点信息更新当前节点 void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]), t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); if(rs[x]) for(int i=0;i<DIM;i++) t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]), t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } //建树 调用时build(root,1,n,0) void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } //【tag未在建树板子中出现】 //将某个节点激活 void insert(int x,int type) { if(t[x]==cur) { t[x].tag=1; return; } now=type; if(cur<t[x]) insert(ls[x],nxt(type)); else insert(rs[x],nxt(type)); } int dist(int x) { int res=0; for(int i=0;i<DIM;i++) res+=max(0,t[x].lb[i]-cur.p[i])+max(0,cur.p[i]-t[x].rb[i]); return res; } //查询距离cur最近点的距离 //调用时为ans=INF, cur=..., mindist(ans,root) void mindist(int &ans,int x) { if(t[x].tag) { int res=0; for(int i=0;i<DIM;i++) res+=abs(t[x].p[i]-cur.p[i]); ans=min(ans,res); } int u=ls[x],L=ls[x]?dist(ls[x]):INF; int v=rs[x],R=rs[x]?dist(rs[x]):INF; if(L>R) swap(u,v),swap(L,R); if(L<ans) mindist(ans,u); if(R<ans) mindist(ans,v); } //【val,sum未在建树板子中出现】 //用两个儿子更新当前节点的值与和 inline void update(int x) { t[x].sum=t[x].val; if(ls[x]) t[x].sum+=t[ls[x]].sum; if(rs[x]) t[x].sum+=t[rs[x]].sum; } //查询某个子矩形内的和(注意坐标有可能爆int) //注意l,r为行 u,d为列 ll query(int x,int l,int r,int u,int d) { if(t[x].rb[0]<l || t[x].lb[0]>r || t[x].rb[1]<u || t[x].lb[1]>d) return 0; if(t[x].lb[0]>=l && t[x].rb[0]<=r && t[x].lb[1]>=u && t[x].rb[1]<=d) return t[x].sum; ll res=0; if(t[x].p[0]>=l && t[x].p[0]<=r && t[x].p[1]>=u && t[x].p[1]<=d) res+=t[x].val; if(ls[x]) res+=query(ls[x],l,r,u,d); if(rs[x]) res+=query(rs[x],l,r,u,d); return res; } };
最近点对模板题:BZOJ 2648 (SJY摆棋子)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } const int INF=1<<30; const int N=1000005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int tag,p[DIM]; int lb[DIM],rb[DIM]; //需要根据维数自定义 Node(int x=0,int y=0,int z=0) { p[0]=x,p[1]=y,tag=z; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; Node a[N]; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].tag=cur.tag; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(a+l,a+x,a+r+1); cur=a[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } void insert(int x,int type) { if(t[x]==cur) { t[x].tag=1; return; } now=type; if(cur<t[x]) insert(ls[x],nxt(type)); else insert(rs[x],nxt(type)); } inline int dist(int x) { int res=0; for(int i=0;i<DIM;i++) res+=max(0,t[x].lb[i]-cur.p[i])+max(0,cur.p[i]-t[x].rb[i]); return res; } inline void mindist(int &ans,int x) { if(t[x].tag) { int res=0; for(int i=0;i<DIM;i++) res+=abs(t[x].p[i]-cur.p[i]); ans=min(ans,res); } int u=ls[x],L=ls[x]?dist(ls[x]):INF; int v=rs[x],R=rs[x]?dist(rs[x]):INF; if(L>R) swap(u,v),swap(L,R); if(L<ans) mindist(ans,u); if(R<ans) mindist(ans,v); } }; KDTree tree; int t[N],x[N],y[N]; int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { a[i].tag=1; read(a[i].p[0]),read(a[i].p[1]); } for(int i=1;i<=m;i++) { read(t[i]),read(x[i]),read(y[i]); if(t[i]==1) a[++n]=Node(x[i],y[i],0); } tree.build(root,1,n,0); for(int i=1;i<=m;i++) { tree.cur=Node(x[i],y[i]); if(t[i]==1) tree.insert(root,0); else { int ans=INF; tree.mindist(ans,root); out(ans),putchar('\n'); } } return 0; }
差不多的一道题,多一个删除:HDU 2966 ($In\ case\ of\ failure$)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(ll x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } const ll INF=1LL<<60; const int N=100005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int tag,p[DIM]; int lb[DIM],rb[DIM]; //需要根据维数自定义 Node(int x=0,int y=0,int z=0) { p[0]=x,p[1]=y,tag=z; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; Node a[N]; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].tag=cur.tag; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(a+l,a+x,a+r+1); cur=a[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } void rev(int x,int type) { if(t[x]==cur) { t[x].tag^=1; return; } now=type; if(cur<t[x]) rev(ls[x],nxt(type)); else rev(rs[x],nxt(type)); } inline ll sq(ll x) { return x*x; } inline ll dist(int x) { ll res=0; for(int i=0;i<DIM;i++) res+=sq(max(0,t[x].lb[i]-cur.p[i]))+sq(max(0,cur.p[i]-t[x].rb[i])); return res; } inline void mindist(ll &ans,int x) { if(t[x].tag) { ll res=0; for(int i=0;i<DIM;i++) res+=sq(abs(t[x].p[i]-cur.p[i])); ans=min(ans,res); } int u=ls[x]; ll L=ls[x]?dist(ls[x]):INF; int v=rs[x]; ll R=rs[x]?dist(rs[x]):INF; if(L>R) swap(u,v),swap(L,R); if(L<ans) mindist(ans,u); if(R<ans) mindist(ans,v); } }; KDTree tree; int x[N],y[N]; int main() { int T; read(T); while(T--) { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { read(x[i]),read(y[i]); a[i]=Node(x[i],y[i],1); tree.ls[i]=tree.rs[i]=0; } tree.build(root,1,n,0); for(int i=1;i<=n;i++) { tree.cur=Node(x[i],y[i]); tree.rev(root,0); ll ans=INF; tree.mindist(ans,root); out(ans),putchar('\n'); tree.rev(root,0); } } return 0; }
求第$k$远点对:Luogu P4357 ($K$远点对,$CQOI2016$)
由于$k$很小,所以不妨对于每个点求$k$次最远点,每求完一次就把最远点删掉;在求完$k$次后再全部恢复
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; typedef pair<ll,ll> pii; inline void read(ll &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(ll x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } const ll INF=-1+(1LL<<61)+(1LL<<61); const int N=100005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int tag; ll p[DIM]; ll lb[DIM],rb[DIM]; Node(ll x=0,ll y=0,int z=0) { p[0]=x,p[1]=y,tag=z; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].tag=cur.tag; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } void modify(int x,int dlt,int type) { if(t[x]==cur) { t[x].tag+=dlt; return; } now=type; if(cur<t[x]) modify(ls[x],dlt,nxt(type)); else modify(rs[x],dlt,nxt(type)); } inline ll dist(int x) { ll res=0; for(int i=0;i<DIM;i++) { ll dmax=max(abs(t[x].lb[i]-cur.p[i]),abs(t[x].rb[i]-cur.p[i])); res+=dmax*dmax; } return res; } inline void query(ll &ans,int &pos,int x) { if(t[x].tag>0) { ll res=0; for(int i=0;i<DIM;i++) res+=(t[x].p[i]-cur.p[i])*(t[x].p[i]-cur.p[i]); if(res>ans) { ans=res; pos=x; } } int u=ls[x]; ll L=ls[x]?dist(ls[x]):0LL; int v=rs[x]; ll R=rs[x]?dist(rs[x]):0LL; if(L<R) swap(u,v),swap(L,R); if(L>ans) query(ans,pos,u); if(R>ans) query(ans,pos,v); } }; KDTree tree; int n,k; pii p[N]; int num[N]; ll a[205]; inline bool cmp(ll x,ll y) { return x>y; } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) read(p[i].first),read(p[i].second); sort(p+1,p+n+1); int m=unique(p+1,p+n+1)-p-1; for(int i=1;i<=n;i++) { int pos=lower_bound(p+1,p+n+1,p[i])-p; num[pos]++; } for(int i=1;i<=m;i++) tree.t[i]=Node(p[i].first,p[i].second,num[i]); tree.build(root,1,m,0); for(int i=1;i<=m;) { tree.cur=tree.t[i]; tree.modify(root,-1,0); vector<int> v; for(int j=1;j<=k && i+j<=n;j++) { int pos=-1; ll ans=a[k]; tree.cur=tree.t[i]; tree.query(ans,pos,root); if(pos<0) break; v.push_back(pos); tree.cur=tree.t[pos]; tree.modify(root,-1,0); a[k+v.size()]=ans; } sort(a+1,a+k+v.size()+1,cmp); for(int j=0;j<v.size();j++) { tree.cur=tree.t[v[j]]; tree.modify(root,1,0); } if(tree.t[i].tag<1) i++; } out(a[k]),putchar('\n'); return 0; }
挺巧妙的题目,将树上问题通过dfs序转成平面上问题:BZOJ 4154 ($Generating\ Synergy$,$Ipsc2015$)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } typedef long long ll; const int MOD=1000000007; const int INF=1<<30; const int N=100005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int c,tag,p[DIM]; int lb[DIM],rb[DIM]; //需要根据维数自定义 Node(int x=0,int y=0,int z=0) { p[0]=x,p[1]=y,tag=z; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; Node a[N]; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].tag=cur.tag; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } inline void pushdown(int x) { if(!t[x].tag) return; t[x].c=t[x].tag; if(ls[x]) t[ls[x]].tag=t[x].tag; if(rs[x]) t[rs[x]].tag=t[x].tag; t[x].tag=0; } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(a+l,a+x,a+r+1); cur=a[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } int query(int x,int type) { pushdown(x); if(t[x]==cur) return t[x].c; now=type; if(cur<t[x]) return query(ls[x],nxt(type)); else return query(rs[x],nxt(type)); } void modify(int x,int l,int r,int u,int d,int color) { pushdown(x); if(t[x].rb[0]<l || t[x].lb[0]>r || t[x].rb[1]<u || t[x].lb[1]>d) return; if(t[x].lb[0]>=l && t[x].rb[0]<=r && t[x].lb[1]>=u && t[x].rb[1]<=d) { t[x].tag=color; return; } if(t[x].p[0]>=l && t[x].p[0]<=r && t[x].p[1]>=u && t[x].p[1]<=d) t[x].c=color; if(ls[x]) modify(ls[x],l,r,u,d,color); if(rs[x]) modify(rs[x],l,r,u,d,color); } }; KDTree tree; int n,c,m; int fa[N]; vector<int> v[N]; int dep[N]; int tot,st[N],ed[N]; void dfs(int x) { st[x]=++tot; dep[x]=dep[fa[x]]+1; for(int i=0;i<v[x].size();i++) dfs(v[x][i]); ed[x]=tot; } int main() { int T; read(T); while(T--) { for(int i=1;i<=n;i++) { v[i].clear(); tree.ls[i]=tree.rs[i]=0; } read(n),read(c),read(m); for(int i=2;i<=n;i++) { int x; read(x); fa[i]=x; v[x].push_back(i); } dfs(1); for(int i=1;i<=n;i++) a[i]=Node(st[i],dep[i],1); tree.build(root,1,n,0); ll ans=0; for(int i=1;i<=m;i++) { int x,y,z; read(x),read(y),read(z); if(z==0) { tree.cur=Node(st[x],dep[x]); int res=tree.query(root,0); // printf("res=%d\n",res); ans=(ans+1LL*i*res)%MOD; } else tree.modify(root,st[x],ed[x],dep[x],dep[x]+y,z); } printf("%lld\n",ans); } return 0; }
也可以处理单点修改区间查询:Luogu P4148 (简单题)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } typedef pair<int,int> pii; const int INF=1<<30; const int N=200005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int val,sum,p[DIM]; int lb[DIM],rb[DIM]; //需要根据维数自定义 Node(int x=0,int y=0,int z=0) { val=z; p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int sz,ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } inline void update(int x) { t[x].sum=t[x].val; if(ls[x]) t[x].sum+=t[ls[x]].sum; if(rs[x]) t[x].sum+=t[rs[x]].sum; } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); update(x); pushup(x); } void insert(int &x,int dlt,int type) { if(!x) { x=++sz; newnode(x); } if(t[x]==cur) { t[x].val+=dlt; update(x); return; } now=type; if(cur<t[x]) insert(ls[x],dlt,nxt(type)); else insert(rs[x],dlt,nxt(type)); update(x); pushup(x); } int query(int x,int l,int r,int u,int d) { if(t[x].rb[0]<l || t[x].lb[0]>r || t[x].rb[1]<u || t[x].lb[1]>d) return 0; if(t[x].lb[0]>=l && t[x].rb[0]<=r && t[x].lb[1]>=u && t[x].rb[1]<=d) return t[x].sum; int res=0; if(t[x].p[0]>=l && t[x].p[0]<=r && t[x].p[1]>=u && t[x].p[1]<=d) res+=t[x].val; if(ls[x]) res+=query(ls[x],l,r,u,d); if(rs[x]) res+=query(rs[x],l,r,u,d); return res; } }; KDTree tree; int main() { int n; read(n); int last_ans=0; while(1) { int op,l,r,u,d; read(op); if(op==3) break; if(op==1) { read(l),read(r),read(u); l^=last_ans,r^=last_ans,u^=last_ans; tree.cur=Node(l,r); tree.insert(root,u,0); if(tree.sz%700==0) tree.build(root,1,tree.sz,0); } else { read(l),read(u),read(r),read(d); l^=last_ans,r^=last_ans,u^=last_ans,d^=last_ans; last_ans=tree.query(root,l,r,u,d); out(last_ans); putchar('\n'); } } return 0; }
同样是区间查询:Luogu P3810 (【模板】三维偏序)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } typedef long long ll; const int INF=1<<30; const int N=100005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int val,sum,p[DIM]; int lb[DIM],rb[DIM]; Node(int x=0,int y=0) { val=sum=0; p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].val=t[x].sum=0; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } inline void update(int x) { t[x].sum=t[x].val; if(ls[x]) t[x].sum+=t[ls[x]].sum; if(rs[x]) t[x].sum+=t[rs[x]].sum; } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); update(x); pushup(x); } void insert(int x,int type) { if(t[x]==cur) { t[x].val++; update(x); return; } now=type; if(cur<t[x]) insert(ls[x],nxt(type)); else insert(rs[x],nxt(type)); update(x); } int query(int x) { if(t[x].lb[0]>cur.p[0] || t[x].lb[1]>cur.p[1]) return 0; if(t[x].rb[0]<=cur.p[0] && t[x].rb[1]<=cur.p[1]) return t[x].sum; int res=0; if(t[x].p[0]<=cur.p[0] && t[x].p[1]<=cur.p[1]) res+=t[x].val; if(ls[x]) res+=query(ls[x]); if(rs[x]) res+=query(rs[x]); return res; } }; KDTree tree; int n,m; struct tri { int p[3]; }a[N]; inline bool operator <(const tri &A,const tri &B) { for(int i=0;i<3;i++) if(A.p[i]!=B.p[i]) return A.p[i]<B.p[i]; return false; } inline bool operator ==(tri &A,tri &B) { for(int i=0;i<3;i++) if(A.p[i]!=B.p[i]) return false; return true; } int ans[N]; int main() { read(n),read(m); for(int i=1;i<=n;i++) { for(int j=0;j<3;j++) read(a[i].p[j]); tree.t[i]=Node(a[i].p[1],a[i].p[2]); } sort(a+1,a+n+1); tree.build(root,1,n,0); for(int i=1,j;i<=n;i=j) { j=i; while(j<=n && a[i]==a[j]) j++; for(int k=0;k<2;k++) tree.cur.p[k]=a[i].p[k+1]; for(int k=i;k<j;k++) tree.insert(root,0); int res=tree.query(root); ans[res]+=j-i; } for(int i=1;i<=n;i++) out(ans[i]),putchar('\n'); return 0; }
稍微麻烦点的题目:HDU 5994 ($Generator\ and\ Monitor$,$2016ICPC$青岛)
如果线段树上的能想清楚,KD树上的也就差不多了
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } const int INF=1<<30; const int N=200005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int p[DIM]; int lb[DIM],rb[DIM]; int id,val,tag,minv,minp; Node(int x=0,int y=0,int z=0,int w=0) { p[0]=x,p[1]=y,id=z,val=w; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int sz,fa[N],ls[N],rs[N]; inline void newnode(int x,int f) { t[x].tag=0; t[x].minp=x; t[x].id=cur.id; t[x].val=t[x].minv=cur.val; fa[x]=f; ls[x]=rs[x]=0; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } inline void pushdown(int x) { if(!t[x].tag) return; if(ls[x]) { t[ls[x]].val+=t[x].tag; t[ls[x]].tag+=t[x].tag; t[ls[x]].minv+=t[x].tag; } if(rs[x]) { t[rs[x]].val+=t[x].tag; t[rs[x]].tag+=t[x].tag; t[rs[x]].minv+=t[x].tag; } t[x].tag=0; } void pushall(int x) { pushdown(x); if(ls[x]) pushall(ls[x]); if(rs[x]) pushall(rs[x]); } inline void update(int x) { t[x].minp=x; t[x].minv=t[x].val; if(ls[x] && t[ls[x]].minv<t[x].minv) { t[x].minv=t[ls[x]].minv; t[x].minp=t[ls[x]].minp; } if(rs[x] && t[rs[x]].minv<t[x].minv) { t[x].minv=t[rs[x]].minv; t[x].minp=t[rs[x]].minp; } } void build(int &x,int l,int r,int type,int f) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x,f); if(l<x) build(ls[x],l,x-1,nxt(type),x); if(x<r) build(rs[x],x+1,r,nxt(type),x); pushup(x); update(x); } void insert(int &x,int type,int f) { pushdown(x); if(!x) { x=++sz; newnode(x,f); return; } now=type; if(cur<t[x]) insert(ls[x],nxt(type),x); else insert(rs[x],nxt(type),x); pushup(x); update(x); } void modify(int x,int y) { pushdown(x); if(t[x].lb[0]>y || t[x].rb[1]<y) return; if(t[x].rb[0]<=y && t[x].lb[1]>=y) { t[x].val--; t[x].tag=-1; pushdown(x); update(x); return; } if(t[x].p[0]<=y && t[x].p[1]>=y) t[x].val--; if(ls[x]) modify(ls[x],y); if(rs[x]) modify(rs[x],y); update(x); } void rev(int x) { vector<int> v; int cur=x; while(cur) { v.push_back(cur); cur=fa[cur]; } for(int i=v.size()-1;i>=0;i--) pushdown(v[i]); t[x].val=INF; for(int i=0;i<v.size();i++) update(v[i]); } }; KDTree tree; int n,m; int main() { int T; read(T); for(int kase=1;kase<=T;kase++) { printf("Case #%d:\n",kase); for(int i=1;i<=tree.sz;i++) tree.ls[i]=tree.rs[i]=tree.fa[i]=0; root=tree.sz=0; int xorsum=0; read(n),read(m); for(int i=1;i<=n;i++) { char op=getchar(); while(op<'A' || op>'Z') op=getchar(); int x,y,z; if(op=='C') { read(x),read(y),read(z); tree.cur=Node(x,y,i,z); tree.insert(root,0,0); if(tree.sz%1200==0) { tree.pushall(root); tree.build(root,1,tree.sz,0,0); } } else { if(!root) continue; read(x); x^=xorsum; tree.modify(root,x); vector<int> v; while(!tree.t[root].minv) { int pos=tree.t[root].minp; v.push_back(tree.t[pos].id); tree.rev(pos); } sort(v.begin(),v.end()); if(v.size()) out(i); for(int j=0;j<v.size();j++) { xorsum^=v[j]; putchar(' '),out(v[j]); } if(v.size()) putchar('\n'); } } } return 0; }
一些总结(主要是卡常):
1. 对于最近点对,查询时先访问 边界离查询点更近 的树节点,能卡掉$2\text{~}4$倍常数
2. 如果题目没有强制要求在线,最好离线后建树;否则考虑定期重构(单次重构$nlogn$,单次查询$max(\sqrt{n},\frac{n}{size})$,稍微计算一下即可)
3. 复杂度是$O(n^{\frac{2k-1}{k}})$,所以维数越高时间复杂度越差;有时候可以像三维偏序一样,通过一些外部的处理去掉一个维度
如果遇到的话,慢慢更一些题目
看到三元组其实就可以往这方面想了:HDU 5517 ($Triple$,$2016ICPC$沈阳)
正解是二维树状数组?那没事了
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; struct tri { int x,y,z,cnt; tri(int a=0,int b=0,int c=0,int d=0) { x=a,y=b,z=c,cnt=d; } }; inline bool operator <(const tri &X,const tri &Y) { if(X.x!=Y.x) return X.x<Y.x; if(X.y!=Y.y) return X.y<Y.y; if(X.z!=Y.z) return X.z<Y.z; return false; } inline bool operator ==(const tri &X,const tri &Y) { return (X.x==Y.x && X.y==Y.y && X.z==Y.z); } inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(int x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } typedef pair<int,int> pii; const int INF=1<<30; const int N=100005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int tag,p[DIM]; int lb[DIM],rb[DIM]; Node(int x=0,int y=0) { p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].tag=cur.tag; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); } void insert(int x,int type) { if(t[x]==cur) { t[x].tag=1; return; } now=type; if(cur<t[x]) insert(ls[x],nxt(type)); else insert(rs[x],nxt(type)); } bool query(int x,int l,int r) { if(t[x].rb[0]<l || t[x].rb[1]<r) return false; if(t[x].tag && t[x].p[0]>=l && t[x].p[1]>=r) return true; bool res=false; if(ls[x]) res|=query(ls[x],l,r); if(rs[x]) res|=query(rs[x],l,r); return res; } }; KDTree tree; int n,m,sz; vector<int> vl[N]; vector<pii> vr[N]; tri point[N]; int main() { int T; read(T); for(int kase=1;kase<=T;kase++) { for(int i=1;i<=100000;i++) vl[i].clear(),vr[i].clear(); read(n),read(m); for(int i=1;i<=n;i++) { int a,b; read(a),read(b); vl[b].push_back(a); } for(int i=1;i<=m;i++) { int c,d,e; read(c),read(d),read(e); vr[e].push_back(pii(c,d)); } sz=0; for(int i=1;i<=100000;i++) { if(vl[i].size()==0) continue; sort(vl[i].begin(),vl[i].end()); int amax=vl[i].back(),cnt=0; for(int j=vl[i].size()-1;j>=0;j--) if(vl[i][j]==amax) cnt++; for(int j=0;j<vr[i].size();j++) point[++sz]=tri(amax,vr[i][j].first,vr[i][j].second,cnt); } sort(point+1,point+sz+1); for(int i=1;i<=sz;i++) { tree.t[i].tag=0; tree.t[i].p[0]=point[i].y; tree.t[i].p[1]=point[i].z; } tree.build(root,1,sz,0); int ans=0; for(int i=sz;i>=1;) { int j=i; while(j>=1 && point[j]==point[i]) j--; int add=i-j; if(!tree.query(root,point[i].y,point[i].z)) ans+=add*point[i].cnt; tree.cur=Node(point[i].y,point[i].z); tree.insert(root,0); i=j; } printf("Case #%d: %d\n",kase,ans); } return 0; }
才反应过来KD树能代替动态主席树:计蒜客T42400 ($Paper\ Grading$,$2019ICPC$南京)
也是一种思路吧,也可以避免离散化
#include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; struct TrieNode { bool end; int to[26]; TrieNode() { end=false; memset(to,0,sizeof(to)); } }; const int N=200005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int val,sum,p[DIM]; int lb[DIM],rb[DIM]; Node(int x=0,int y=0,int z=0) { val=z; p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N*10]; int sz,ls[N*10],rs[N*10]; inline void newnode(int x) { ls[x]=rs[x]=0; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } inline void update(int x) { t[x].sum=t[x].val; if(ls[x]) t[x].sum+=t[ls[x]].sum; if(rs[x]) t[x].sum+=t[rs[x]].sum; } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); update(x); pushup(x); } void insert(int &x,int dlt,int type) { if(!x) { x=++sz; newnode(x); } if(t[x]==cur) { t[x].val+=dlt; update(x); return; } now=type; if(cur<t[x]) insert(ls[x],dlt,nxt(type)); else insert(rs[x],dlt,nxt(type)); update(x); pushup(x); } int query(int x,int l,int r,int u,int d) { if(t[x].rb[0]<l || t[x].lb[0]>r || t[x].rb[1]<u || t[x].lb[1]>d) return 0; if(t[x].lb[0]>=l && t[x].rb[0]<=r && t[x].lb[1]>=u && t[x].rb[1]<=d) return t[x].sum; int res=0; if(t[x].p[0]>=l && t[x].p[0]<=r && t[x].p[1]>=u && t[x].p[1]<=d) res+=t[x].val; if(ls[x]) res+=query(ls[x],l,r,u,d); if(rs[x]) res+=query(rs[x],l,r,u,d); return res; } }; int n,m,len; string s[N]; int top=0; TrieNode trie[N]; int insert(int x) { int cur=0; for(int i=0;i<s[x].length();i++) { int &nxt=trie[cur].to[s[x][i]-'a']; if(nxt==0) nxt=++top; cur=nxt; } trie[cur].end=true; return cur; } int tot; int st[N],ed[N]; void label(int x) { if(trie[x].end) st[x]=ed[x]=++tot; for(int i=0;i<26;i++) { int nxt=trie[x].to[i]; if(nxt) { label(nxt); if(!st[x]) st[x]=st[nxt]; ed[x]=ed[nxt]; } } } int sz; KDTree tree; int x[N],y[N]; int K[N],L[N],R[N]; int opt[N],dfn[N],val[N]; string q[N]; int main() { ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) { cin>>s[i]; val[i]=i; insert(i); } label(0); for(int i=1;i<=n;i++) { int cur=0; for(int j=0;j<s[i].length();j++) cur=trie[cur].to[s[i][j]-'a']; dfn[i]=st[cur]; tree.t[++sz]=Node(dfn[i],i,1); } for(int i=1;i<=m;i++) { cin>>opt[i]; if(opt[i]==1) { cin>>x[i]>>y[i]; swap(val[x[i]],val[y[i]]); tree.t[++sz]=Node(dfn[val[x[i]]],x[i],0); tree.t[++sz]=Node(dfn[val[y[i]]],y[i],0); } else cin>>q[i]>>K[i]>>L[i]>>R[i]; } tree.build(root,1,sz,0); for(int i=1;i<=n;i++) val[i]=i; for(int i=1;i<=m;i++) { if(opt[i]==1) { tree.cur=Node(dfn[val[x[i]]],x[i]); tree.insert(root,-1,0); tree.cur=Node(dfn[val[y[i]]],y[i]); tree.insert(root,-1,0); swap(val[x[i]],val[y[i]]); tree.cur=Node(dfn[val[x[i]]],x[i]); tree.insert(root,1,0); tree.cur=Node(dfn[val[y[i]]],y[i]); tree.insert(root,1,0); } else { int cur=0; for(int j=0;j<K[i];j++) { int nxt=trie[cur].to[q[i][j]-'a']; if(!nxt) { cur=0; break; } else cur=nxt; } if(cur==0 && K[i]!=0) cout<<0<<'\n'; else cout<<tree.query(root,st[cur],ed[cur],L[i],R[i])<<'\n'; } } return 0; }
还是代替动态主席树:计蒜客T42574 ($Yuuki\ and\ a\ problem$,$2019ICPC$徐州)
不过这题出的一个问题是KD树上的坐标范围可能超过int,所以需要取min来限制
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int DIM=2; const int N=200005; inline int nxt(int x){ if(++x==DIM) x=0; return x; } int now; struct Node{ ll val,sum; int p[DIM]; int lb[DIM],rb[DIM]; Node(int x=0,int y=0,ll z=0) {p[0]=x,p[1]=y,val=z,sum=0;} }; inline bool operator <(const Node &X,const Node &Y){ int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y){ for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree{ Node cur,t[N<<1]; int ls[N<<1],rs[N<<1]; inline void newnode(int x){ ls[x]=rs[x]=0; t[x].val=t[x].sum=cur.val; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } inline void update(int x){ t[x].sum=t[x].val; if(ls[x]) t[x].sum+=t[ls[x]].sum; if(rs[x]) t[x].sum+=t[rs[x]].sum; } inline void pushup(int x){ if(ls[x]) for(int i=0;i<DIM;i++) t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]), t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); if(rs[x]) for(int i=0;i<DIM;i++) t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]), t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } void build(int &x,int l,int r,int type){ x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); update(x); } void insert(int x,int dlt,int type){ if(t[x]==cur){ t[x].val+=dlt; update(x); return; } now=type; if(cur<t[x]) insert(ls[x],dlt,nxt(type)); else insert(rs[x],dlt,nxt(type)); update(x); } ll query(int x,int l,int r,int u,int d) { if(t[x].rb[0]<l || t[x].lb[0]>r || t[x].rb[1]<u || t[x].lb[1]>d) return 0; if(t[x].lb[0]>=l && t[x].rb[0]<=r && t[x].lb[1]>=u && t[x].rb[1]<=d) return t[x].sum; ll res=0; if(t[x].p[0]>=l && t[x].p[0]<=r && t[x].p[1]>=u && t[x].p[1]<=d) res+=t[x].val; if(ls[x]) res+=query(ls[x],l,r,u,d); if(rs[x]) res+=query(rs[x],l,r,u,d); return res; } }; KDTree tree; int n,q,sz; int a[N]; int op[N],L[N],R[N]; int main() { scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); tree.t[++sz]=Node(i,a[i],a[i]); } for(int i=1;i<=q;i++) { scanf("%d%d%d",&op[i],&L[i],&R[i]); if(op[i]==1) tree.t[++sz]=Node(L[i],R[i],0); } tree.build(root,1,sz,0); for(int i=1;i<=q;i++) if(op[i]==1) { tree.cur=Node(L[i],a[L[i]]); tree.insert(root,-a[L[i]],0); tree.cur=Node(L[i],R[i]); tree.insert(root,R[i],0); a[L[i]]=R[i]; } else { ll x=0,res=0; while(1) { res=tree.query(root,L[i],R[i],1,min(x+1,(ll)N)); if(res==x) break; x=res; } printf("%lld\n",x+1); } return 0; }
Nowcoder 4120I ($Practice\ for\ KD\ Tree$,$2020\ Wannafly\ Winter\ Camp$)
结果现场就我一个是KD树艹过去的...(按照dls的意思KD树应该过不了)
在query时记得根据$maxv$调换查询$L,R$的顺序以卡常(事实证明非常有效)
#include <cstdio> #include <locale> #include <vector> #include <cstring> #include <algorithm> using namespace std; inline void read(int &x) { x=0; int rev=1; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); if(ch=='-') rev=-1,ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); x*=rev; } inline void out(long long x) { if(x==0) { putchar('0'); return; } int len=0; static char buff[20]; while(x) buff[++len]=x%10+'0',x/=10; while(len) putchar(buff[len--]); } typedef long long ll; const int N=4000005; const int M=2005; const int DIM=2; inline int nxt(int x) { if(++x==DIM) x=0; return x; } int now; struct Node { int p[DIM]; int lb[DIM],rb[DIM]; ll val,maxv; Node(int x=0,int y=0,ll z=0) { val=maxv=z; p[0]=x,p[1]=y; } }; inline bool operator <(const Node &X,const Node &Y) { int i=now; if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; for(int i=nxt(now);i!=now;i=nxt(i)) if(X.p[i]!=Y.p[i]) return X.p[i]<Y.p[i]; return false; } inline bool operator ==(const Node &X,const Node &Y) { for(int i=0;i<DIM;i++) if(X.p[i]!=Y.p[i]) return false; return true; } int root; struct KDTree { Node cur,t[N]; int ls[N],rs[N]; inline void newnode(int x) { ls[x]=rs[x]=0; t[x].val=cur.val; t[x].maxv=cur.maxv; for(int i=0;i<DIM;i++) t[x].lb[i]=t[x].rb[i]=t[x].p[i]=cur.p[i]; } void pushup(int x) { if(ls[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[ls[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[ls[x]].rb[i]); } if(rs[x]) for(int i=0;i<DIM;i++) { t[x].lb[i]=min(t[x].lb[i],t[rs[x]].lb[i]); t[x].rb[i]=max(t[x].rb[i],t[rs[x]].rb[i]); } } void build(int &x,int l,int r,int type) { x=(l+r)>>1; now=type; nth_element(t+l,t+x,t+r+1); cur=t[x]; newnode(x); if(l<x) build(ls[x],l,x-1,nxt(type)); if(x<r) build(rs[x],x+1,r,nxt(type)); pushup(x); update(x); } void update(int x) { t[x].maxv=t[x].val; if(ls[x]) t[x].maxv=max(t[x].maxv,t[ls[x]].maxv); if(rs[x]) t[x].maxv=max(t[x].maxv,t[rs[x]].maxv); } void query(int x,ll &ans) { if(t[x].maxv<=ans) return; if(t[x].rb[0]<cur.lb[0] || t[x].lb[0]>cur.rb[0]) return; if(t[x].rb[1]<cur.lb[1] || t[x].lb[1]>cur.rb[1]) return; if(t[x].lb[0]>=cur.lb[0] && t[x].rb[0]<=cur.rb[0] && t[x].lb[1]>=cur.lb[1] && t[x].rb[1]<=cur.rb[1]) { ans=max(ans,t[x].maxv); return; } if(t[x].p[0]>=cur.lb[0] && t[x].p[0]<=cur.rb[0] && t[x].p[1]>=cur.lb[1] && t[x].p[1]<=cur.rb[1]) ans=max(ans,t[x].val); int L=ls[x],R=rs[x]; if(t[L].maxv<t[R].maxv) swap(L,R); if(L) query(L,ans); if(R) query(R,ans); } }tree; int n,m1,m2; ll a[M][M]; int main() { read(n),read(m1),read(m2); tree.build(root,1,n*n,0); for(int i=1;i<=m1;i++) { int x1,y1,x2,y2,w; read(x1),read(y1),read(x2),read(y2),read(w); a[x2][y2]+=w; a[x1-1][y2]-=w; a[x2][y1-1]-=w; a[x1-1][y1-1]+=w; } for(int i=2*n;i>=1;i--) { for(int j=min(n,i-1);j>=max(1,i-n);j--) { int y=j,x=i-j; a[x][y]+=a[x+1][y]+a[x][y+1]-a[x+1][y+1]; tree.t[(x-1)*n+y]=Node(x,y,a[x][y]); } } tree.build(root,1,n*n,0); for(int i=1;i<=m2;i++) { int x1,y1,x2,y2; read(x1),read(y1),read(x2),read(y2); tree.cur.lb[0]=x1,tree.cur.rb[0]=x2; tree.cur.lb[1]=y1,tree.cur.rb[1]=y2; ll ans=0; tree.query(root,ans); out(ans),putchar('\n'); } return 0; }
(完)