K-Dtree
多维维护问题,每层换一维进行统计。
bzoj4066 简单题
题目大意:给(x,y)+A,求(x1,y1)~(x2,y2)的权值和。
思路:kd-tree,但这道题目中重建是特判的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 using namespace std; inline int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} int dd,rt,tt=0; struct use{ int d[2],mn[2],mx[2],sm,v,l,r; int &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1];} bool operator<(const use &x)const{return d[dd]<x.d[dd];} }tr[N],nt,ci[N]; inline bool bin(int x1,int y1,int x2,int y2,int a1,int b1,int a2,int b2){ return x1<=a1&&y1<=b1&&x2>=a2&&y2>=b2; } inline bool bout(int x1,int y1,int x2,int y2,int a1,int b1,int a2,int b2){ return x1>a2||x2<a1||y1>b2||y2<b1; } void updata(int x){ int i,l,r;l=tr[x].l;r=tr[x].r; for (i=0;i<2;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); } }tr[x].sm=tr[x].v+tr[l].sm+tr[r].sm;} void ins(int &x,int d){ if (!x){x=++tt;tr[x]=nt;return;} if (tr[x]==nt){tr[x].v+=nt.v;tr[x].sm+=nt.v;return;} if (nt[d]<tr[x][d]) ins(tr[x].l,d^1); else ins(tr[x].r,d^1); updata(x); } int query(int x,int d,int x1,int y1,int x2,int y2){ if (!x) return 0; if (bin(x1,y1,x2,y2,tr[x].mn[0],tr[x].mn[1],tr[x].mx[0],tr[x].mx[1])) return tr[x].sm; if (bout(x1,y1,x2,y2,tr[x].mn[0],tr[x].mn[1],tr[x].mx[0],tr[x].mx[1])) return 0; int sm=0; if (bin(x1,y1,x2,y2,tr[x][0],tr[x][1],tr[x][0],tr[x][1])) sm+=tr[x].v; sm+=query(tr[x].l,d^1,x1,y1,x2,y2)+query(tr[x].r,d^1,x1,y1,x2,y2); return sm;} int rebuild(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=rebuild(l,mid-1,d^1); tr[mid].r=rebuild(mid+1,r,d^1); updata(mid);return mid;} int main(){ int op,x1,y1,x2,y2,v,i,la=0,m=10000; op=in();tr[0]=(use){0,0,0,0,0,0,0,0,0,0}; while((op=in())!=3){ if (op==1){ x1=in()^la;y1=in()^la;v=in()^la; nt=(use){x1,y1,x1,y1,x1,y1,v,v,0,0}; ins(rt,0); if (tt==m){ for (i=1;i<=tt;++i) ci[i]=tr[i]; rt=rebuild(1,tt,0);m+=10000; } }else{ x1=in()^la;y1=in()^la;x2=in()^la;y2=in()^la; la=query(rt,0,x1,y1,x2,y2); printf("%d\n",la); } } }
bzoj1790 矩形藏宝地
题目大意:问有多少个矩形被包含。
思路:用kdtree维护三维空间的最小值,每次查询(1~x1-1,x2~max,y2~max)中最小值是否<y1,如果满足就会被包含。这样只需要第一次build就可以了,不用ins,也就不会被卡成树高的情况。(!!)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 #define inf 2100000000 #define up 3 using namespace std; struct uu{int x1,y1,x2,y2;}ai[N]; int dd,rt,ni; inline int in(){ char ch=getchar();int x=0,f=1; while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-'){f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x*f;} inline int gmn(int a,int b){return (a<b ? a : b);} inline int gmx(int a,int b){return (a>b ? a : b);} struct use{ int d[3],mn[3],mx[3],sm,v,l,r; int &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1]&&d[2]==x.d[2];} bool operator<(const use&x)const{return d[dd]<x.d[dd];} }ci[N],tr[N],nt; void updata(int x){ int l,r,i;l=tr[x].l;r=tr[x].r; for (i=0;i<up;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=gmn(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=gmx(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=gmn(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=gmx(tr[x].mx[i],tr[r].mx[i]); } }tr[x].sm=gmn(tr[x].v,gmn(tr[l].sm,tr[r].sm));} inline bool bin(int x,int x2,int y,int y2,int z,int z2,int a,int a2,int b,int b2,int c,int c2){ return (x<=a&&a2<=x2&&y<=b&&b2<=y2&&z<=c&&c2<=z2); } inline bool bou(int x,int x2,int y,int y2,int z,int z2,int a,int a2,int b,int b2,int c,int c2){ return (x2<a||x>a2||y2<b||y>b2||z2<c||z>c2); } int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,(d+1)%up); tr[mid].r=build(mid+1,r,(d+1)%up); updata(mid);return mid;} int query(int x,int x1,int x2,int y1,int y2,int z1,int z2){ if (!x||tr[x].sm>=ni) return inf; if (bin(x1,x2,y1,y2,z1,z2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1], tr[x].mn[2],tr[x].mx[2])) return tr[x].sm; if (bou(x1,x2,y1,y2,z1,z2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1], tr[x].mn[2],tr[x].mx[2])) return inf; int sm=inf; if (bin(x1,x2,y1,y2,z1,z2,tr[x].d[0],tr[x].d[0],tr[x].d[1],tr[x].d[1], tr[x].d[2],tr[x].d[2])){ sm=tr[x].v;if (sm<ni) return sm; }int l=tr[x].l;int r=tr[x].r; if (tr[l].sm>tr[r].sm) swap(l,r); if (tr[l].sm<ni){ sm=min(sm,query(l,x1,x2,y1,y2,z1,z2)); if (sm<ni) return sm; }if (tr[r].sm<ni) sm=min(sm,query(r,x1,x2,y1,y2,z1,z2)); return sm;} int main(){ int n,i,j,ans=0,x1,y1,x2,y2,bz=0;n=in(); tr[0]=(use){0,0,0,0,0,0,0,0,0,inf,inf,0,0}; for (i=1;i<=n;++i){ x1=in()+1;y1=in()+1;x2=in()+1;y2=in()+1; ai[i]=(uu){x1,y1,x2,y2}; bz=gmx(bz,gmx(x2,y2)); ci[i]=(use){x1,x2,y2,x1,x2,y2,x1,x2,y2,y1,y1,0,0}; }rt=build(1,n,0); for (i=1;i<=n;++i){ x1=ai[i].x1;y1=ai[i].y1;x2=ai[i].x2;y2=ai[i].y2; if (x1>1){ ni=y1;ans+=((j=query(rt,1,x1-1,x2,bz,y2,bz))<ni); } }printf("%d\n",ans); }
(一开始想的都是排x1之后,ins、查询,都会被卡)
bzoj1941 Hide and Seek
题目大意:求平面上某个点距离最远点-最近点最小的值。
思路:kdtree求最近最远点。
注意:求最近点对的时候估计函数的最小值是在点到矩形边界的垂线距离上取的(计算方式很神奇!);最远点对的是和矩形的顶点取的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 500005 #define inf 2100000000 using namespace std; struct uu{int x,y;}ai[N]; int dd,rt=0,tt=0,dmn,dmx; inline int in(){ char ch=getchar();int x=0,f=1; while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-'){f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x*f;} inline int ab(int x){return (x<0 ? -x : x);} struct use{ int d[2],mn[2],mx[2],l,r; int &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1];} bool operator<(const use&x)const{return d[dd]<x.d[dd];} }ci[N],tr[N],nt; void updata(int x){ int l,r,i;l=tr[x].l;r=tr[x].r; for (i=0;i<2;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); } } } int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,d^1); tr[mid].r=build(mid+1,r,d^1); updata(mid);return mid;} inline int dis(use x,use y){return (ab(x[0]-y[0])+ab(x[1]-y[1]));} inline int gmn(use x,use y){ int cc=0,i; for (i=0;i<2;++i) cc+=max(0,x.mn[i]-y[i])+max(0,y[i]-x.mx[i]); return cc;} void qmn(int x,int d,use y){ if (!x) return; if (!(tr[x]==y)) dmn=min(dmn,dis(tr[x],y)); int dl,dr,l,r; dl=gmn(tr[l=tr[x].l],y); dr=gmn(tr[r=tr[x].r],y); if (dl>dr){swap(dl,dr);swap(l,r);} if (dl<dmn) qmn(l,d^1,y); if (dr<dmn) qmn(r,d^1,y); } inline int gmx(use x,use y){ return max(max(ab(x.mn[0]-y[0])+ab(x.mn[1]-y[1]),ab(x.mn[0]-y[0])+ab(x.mx[1]-y[1])), max(ab(x.mx[0]-y[0])+ab(x.mn[1]-y[1]),ab(x.mx[0]-y[0])+ab(x.mx[1]-y[1]))); } void qmx(int x,int d,use y){ if (!x) return; dmx=max(dmx,dis(tr[x],y)); int dl,dr,l,r; dl=gmx(tr[l=tr[x].l],y); dr=gmx(tr[r=tr[x].r],y); if (dl<dr){swap(dl,dr);swap(l,r);} if (dl>dmx) qmx(l,d^1,y); if (dr>dmx) qmx(r,d^1,y); } int main(){ int i,n,x,y,mn=inf;n=in(); tr[0]=(use){0,0,0,0,0,0,0,0}; for (i=1;i<=n;++i){ x=in();y=in(); ai[i]=(uu){x,y}; ci[i]=(use){x,y,x,y,x,y,0,0}; }rt=build(1,n,0); for (i=1;i<=n;++i){ dmn=inf;dmx=0; qmn(rt,0,(use){ai[i].x,ai[i].y,0,0,0,0,0,0}); qmx(rt,0,(use){ai[i].x,ai[i].y,0,0,0,0,0,0}); mn=min(mn,dmx-dmn); }printf("%d\n",mn); }
bzoj4154 Generating Synergy
题目大意:给一棵树,支持:(1)修改某个点x的子树内到x距离不超过l的点的颜色为c;(2)查某个点的颜色。
思路:先build每个点,每个点是一个dfs序和深度的点,修改就是在dfs序的子树和深度限制的范围内放标记修改,查询某个点的颜色。
注意:为了防止出现某个坐标一样的时候另一个坐标随机分配的情况,在比较函数中要双关键字排序。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define LL long long #define p 1000000007 using namespace std; int point[N],next[N],en[N],dn[N][2],dt=0,dd,tt=0,rt=0,dep[N]={0},tot=0; int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} struct use{ int d[2],mn[2],mx[2],v,del,l,r; int &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1];} bool operator<(const use&x)const{ return (d[dd]==x.d[dd] ? d[dd^1]<x.d[dd^1] : d[dd]<x.d[dd]); } }tr[N],ci[N]; void add(int u,int v){next[++tot]=point[u];point[u]=tot;en[tot]=v;} void pre(int u,int ff){ int i;dep[u]=dep[ff]+1;dn[u][0]=++dt; ci[dt]=(use){dt,dep[u],dt,dep[u],dt,dep[u],1,0,0,0}; for (i=point[u];i;i=next[i]) pre(en[i],u); dn[u][1]=dt;} void updata(int x){ int i,l,r;l=tr[x].l;r=tr[x].r; for (i=0;i<2;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); } } } void pushdown(int x){ int l,r;l=tr[x].l;r=tr[x].r; if (l) tr[l].v=tr[l].del=tr[x].del; if (r) tr[r].v=tr[r].del=tr[x].del; tr[x].del=0;} int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,d^1); tr[mid].r=build(mid+1,r,d^1); updata(mid);return mid;} bool bin(int x1,int x2,int y1,int y2,int a1,int a2,int b1,int b2){ return (x1<=a1&&a2<=x2&&y1<=b1&&b2<=y2); } bool bout(int x1,int x2,int y1,int y2,int a1,int a2,int b1,int b2){ return (x1>a2||a1>x2||y1>b2||b1>y2); } void tch(int x,int x1,int x2,int y1,int y2,int c){ if (tr[x].del==c) return; if (bin(x1,x2,y1,y2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1])){ tr[x].v=tr[x].del=c;return; }if (bout(x1,x2,y1,y2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1])) return; if (bin(x1,x2,y1,y2,tr[x][0],tr[x][0],tr[x][1],tr[x][1])) tr[x].v=c; if (tr[x].del) pushdown(x); if (tr[x].l) tch(tr[x].l,x1,x2,y1,y2,c); if (tr[x].r) tch(tr[x].r,x1,x2,y1,y2,c);} int query(int x,int d,use y){ if (tr[x]==y) return tr[x].v; if (tr[x].del) pushdown(x); if (y[d]<tr[x][d]||(y[d]==tr[x][d]&&y[d^1]<tr[x][d^1])) return query(tr[x].l,d^1,y); else return query(tr[x].r,d^1,y);} int main(){ int t,i,n,c,q,x,l,ans; use cc;t=in(); tr[0]=(use){0,0,0,0,0,0,0,0,0,0}; while(t--){ n=in();c=in();q=in(); tot=dt=ans=rt=tt=0; memset(point,0,sizeof(point)); for (i=2;i<=n;++i){ x=in();add(x,i); }pre(1,0);rt=build(1,n,0); for (i=1;i<=q;++i){ x=in();l=in();c=in(); if (!c){ cc=(use){dn[x][0],dep[x],0,0,0,0,0,0,0,0}; x=query(rt,0,cc); ans=(ans+(int)((LL)i*(LL)x%p))%p; }else tch(rt,dn[x][0],dn[x][1],dep[x],dep[x]+l,c); }printf("%d\n",ans); } }
bzoj4520 K远点对
题目大意:求n个点的第k远距离。
思路:用一个堆求出前2k远的距离(2k是因为一对点的距离会算两边),kdtree暴力。
注意:要在堆中先加一个0,否则可能出现某些较优解搜不到。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define N 100005 #define LL long long using namespace std; inline int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} struct uu{LL x,y;}ai[N]; int rt,dd,qz=0,k; priority_queue<LL> que; struct use{ LL d[2],mn[2],mx[2];int l,r; LL &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1];} bool operator<(const use&x)const{ return (d[dd]==x.d[dd] ? d[dd^1]<x.d[dd^1] : d[dd]<x.d[dd]); } }tr[N],ci[N]; void updata(int x){ int i,l,r;l=tr[x].l;r=tr[x].r; for (i=0;i<2;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); } } } int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,d^1); tr[mid].r=build(mid+1,r,d^1); updata(mid);return mid;} inline LL sqr(LL x){return x*x;} inline LL dis(use x,use y){return sqr(x[0]-y[0])+sqr(x[1]-y[1]);} inline LL gmx(use x,use y){ return max(max(sqr(x.mn[0]-y[0])+sqr(x.mn[1]-y[1]),sqr(x.mn[0]-y[0])+sqr(x.mx[1]-y[1])), max(sqr(x.mx[0]-y[0])+sqr(x.mn[1]-y[1]),sqr(x.mx[0]-y[0])+sqr(x.mx[1]-y[1]))); } void find(int x,use y){ if (!x) return; LL dl,dr;int l,r; que.push(-dis(tr[x],y));++qz; if (qz>k){--qz;que.pop();} dl=gmx(tr[l=tr[x].l],y); dr=gmx(tr[r=tr[x].r],y); if (dl<dr){swap(l,r);swap(dl,dr);} if (dl>-que.top()) find(l,y); if (dr>-que.top()) find(r,y); } int main(){ int n,i;LL x,y;n=in();k=in()<<1; for (i=1;i<=n;++i){ x=(LL)in();y=(LL)in(); ai[i]=(uu){x,y}; ci[i]=(use){x,y,x,y,x,y,0,0}; }rt=build(1,n,0); qz=1;que.push(0LL); for (i=1;i<=n;++i){ tr[0]=(use){ai[i].x,ai[i].y,ai[i].x,ai[i].y,ai[i].x,ai[i].y,0,0}; find(rt,(use){ai[i].x,ai[i].y,0,0,0,0,0,0}); }printf("%I64d\n",-que.top()); }
bzoj4538 网络
题目大意:给定一棵树,支持:(1)给加一条权值为x的链;(2)删掉之前加的某一条链;(3)求不经过某点的链的最大权值。
思路:考虑不经过某点x的链有两种情况:两个点都不在x的子树中(保证链的起点的dfs序小于终点的,反应到dfs序上有三种情况),两个点都在且他们的lca不是x。可以用三维kdtree维护,但是会tle。考虑降维:对于点都在子树中的情况,就是考虑lca的限制,所以可以把权值放到lca上,查询的时候查子树中的最大值,因为有删除操作,可以对每个点建一个动态开点线段树,维护这个点上的权值信息。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 200005 #define S 4000005 #define up 20 using namespace std; int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} int dd,rt,tot=0,point[N],next[M],en[M],dn[N][2],dt=0,fa[N][up]={0},dep[N]={0},cz=0,gi[M],mx, te[N<<2],sr[N]={0}; struct tree{int l,r,v;}se[S]; struct uu{int op,u,v,x,lc;}qu[M]; void add(int u,int v){ next[++tot]=point[u];point[u]=tot;en[tot]=v; next[++tot]=point[v];point[v]=tot;en[tot]=u;} void dfs(int u,int ff){ int i,v;dep[u]=dep[ff]+1; fa[u][0]=ff;dn[u][0]=++dt; for (i=1;i<up;++i) fa[u][i]=fa[fa[u][i-1]][i-1]; for (i=point[u];i;i=next[i]){ if ((v=en[i])==ff) continue; dfs(v,u); }dn[u][1]=dt;} int lca(int u,int v){ if (dep[u]<dep[v]) swap(u,v); int i; for (i=up-1;i>=0;--i) if (dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if (u==v) return u; for (i=up-1;i>=0;--i) if (fa[u][i]!=fa[v][i]){ u=fa[u][i];v=fa[v][i]; }return fa[u][0];} void stch(int &i,int la,int l,int r,int x,int y){ se[i=++dt]=se[la]; if (l==r){se[i].v=y;return;} int mid=(l+r)>>1; if (x<=mid) stch(se[i].l,se[la].l,l,mid,x,y); else stch(se[i].r,se[la].r,mid+1,r,x,y); se[i].v=max(se[se[i].l].v,se[se[i].r].v);} void ttch(int i,int l,int r,int x,int y){ if (l==r){te[i]=y;return;} int mid=(l+r)>>1; if (x<=mid) ttch(i<<1,l,mid,x,y); else ttch(i<<1|1,mid+1,r,x,y); te[i]=max(te[i<<1],te[i<<1|1]);} void task(int i,int l,int r,int ll,int rr){ if (ll<=l&&r<=rr){mx=max(mx,te[i]);return;} int mid=(l+r)>>1; if (ll<=mid) task(i<<1,l,mid,ll,rr); if (rr>mid) task(i<<1|1,mid+1,r,ll,rr);} struct use{ int d[2],mn[2],mx[2],sm,v,l,r,f,id; int &operator[](int x){return d[x];} bool operator==(const use&x)const{return d[0]==x.d[0]&&d[1]==x.d[1]&&v==x.v;} bool operator<(const use&x)const{return d[dd]<x.d[dd];} }ci[M],tr[M]; void updata(int x){ int l,r,i;l=tr[x].l;r=tr[x].r; if (l) tr[l].f=x; if (r) tr[r].f=x; for (i=0;i<2;++i){ tr[x].mn[i]=tr[x].mx[i]=tr[x][i]; if (l){ tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); }if (r){ tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); } }tr[x].sm=max(tr[x].v,max(tr[l].sm,tr[r].sm));} int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,d^1); tr[mid].r=build(mid+1,r,d^1); updata(mid);return mid;} void tch(int x){ for (;x;x=tr[x].f) tr[x].sm=max(tr[x].v,max(tr[tr[x].l].sm,tr[tr[x].r].sm)); } bool bin(use x,use y){ return (x.mn[0]<=y.mn[0]&&y.mx[0]<=x.mx[0]&& x.mn[1]<=y.mn[1]&&y.mx[1]<=x.mx[1]);} bool bou(use x,use y){ return (x.mx[0]<y.mn[0]||y.mx[0]<x.mn[0]|| x.mx[1]<y.mn[1]||y.mx[1]<x.mn[1]);} void query(int x,use y){ if (!x||tr[x].sm<=mx) return; if (bin(y,tr[x])){mx=max(mx,tr[x].sm);return;} if (bou(y,tr[x])) return; if (bin(y,(use){0,0,tr[x][0],tr[x][1],tr[x][0],tr[x][1], 0,0,0,0,0,0})) mx=max(mx,tr[x].v); int l=tr[x].l;int r=tr[x].r; if (tr[l].sm<tr[r].sm) swap(l,r); if (tr[l].sm>mx) query(l,y); if (tr[r].sm>mx) query(r,y);} int main(){ int n,m,i,j,u,v,x,lc,d0,d1;n=in();m=in(); for (i=1;i<n;++i){u=in();v=in();add(u,v);} dfs(1,0);tr[0]=(use){0,0,0,0,0,0,-1,-1,0,0,0,0}; for (i=1;i<=m;++i){ if ((j=in())==0){ u=in();v=in();x=in(); if (dn[u][0]>dn[v][0]) swap(u,v); lc=lca(u,v);qu[i]=(uu){j,u,v,x,lc}; ci[++cz]=(use){dn[u][0],dn[v][0],dn[u][0],dn[v][0], dn[u][0],dn[v][0],-1,-1,0,0,0,i}; }else qu[i]=(uu){j,in(),0,0,0}; }rt=build(1,cz,0); for (i=(N<<2)-1;i;--i) te[i]=-1; dt=0;se[0]=(tree){0,0,-1}; for (i=1;i<=cz;++i) gi[tr[i].id]=i; for (i=1;i<=m;++i){ if (!qu[i].op){ u=qu[i].u;v=qu[i].v;lc=qu[i].lc; tr[gi[i]].v=x=qu[i].x;tch(gi[i]); stch(sr[lc],sr[lc],1,m,i,x); ttch(1,1,n,dn[lc][0],se[sr[lc]].v); }if (qu[i].op==1){ j=qu[i].u;tr[gi[j]].v=-1;tch(gi[j]); lc=qu[j].lc; stch(sr[lc],sr[lc],1,m,j,-1); ttch(1,1,n,dn[lc][0],se[sr[lc]].v); }if (qu[i].op==2){ mx=-1;u=qu[i].u;d0=dn[u][0];d1=dn[u][1]; if (d0<d1) task(1,1,n,d0+1,d1); if (d0>1) query(rt,(use){0,0,1,1,d0-1,d0-1,0,0,0,0,0,0}); if (d0>1&&d1<n) query(rt,(use){0,0,1,d1+1,d0-1,n,0,0,0,0,0,0}); if (d1<n) query(rt,(use){0,0,d1+1,d1+1,n,n,0,0,0,0,0,0}); printf("%d\n",mx); } } }
bzoj4553 序列
题目大意:给定一个序列,每次至多变一个数,问所有变化序列中都是不降序列的最长长度。
思路:考虑dp更新的时候要求aj<=minbi&&maxbj<=ai(ai是原数列,bi是包含原数列和修改的最大最小值),可以用kd树维护。
注意:(1)所有变化序列也包含原序列;
(2)kdtree求最值的时候要用这个点的值更新,更新完不要return。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define up 2 using namespace std; inline int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} int dd,rt,ai[N],bx[N],bn[N],gi[N],mx; struct use{ int d[up],mn[up],mx[up],v,vx,l,r,f,p; int &operator[](int x){return d[x];} bool operator<(const use&x)const{return d[dd]<x.d[dd];} }tr[N],ci[N]; void updata(int x){ int l,r,i;l=tr[x].l;r=tr[x].r; if (l) tr[l].f=x; if (r) tr[r].f=x; for (i=0;i<up;++i){ tr[x].mx[i]=tr[x].mn[i]=tr[x][i]; if (l){ tr[x].mx[i]=max(tr[x].mx[i],tr[l].mx[i]); tr[x].mn[i]=min(tr[x].mn[i],tr[l].mn[i]); }if (r){ tr[x].mx[i]=max(tr[x].mx[i],tr[r].mx[i]); tr[x].mn[i]=min(tr[x].mn[i],tr[r].mn[i]); } }tr[x].vx=max(tr[x].v,max(tr[l].vx,tr[r].vx)); } int build(int l,int r,int d){ if (l>r) return 0; int mid=(l+r)>>1; dd=d;nth_element(ci+l,ci+mid,ci+r+1); tr[mid]=ci[mid]; tr[mid].l=build(l,mid-1,d^1); tr[mid].r=build(mid+1,r,d^1); updata(mid);return mid;} void tch(int i,int x){ for (tr[i].v=x;i;i=tr[i].f) tr[i].vx=max(tr[i].v,max(tr[tr[i].l].vx,tr[tr[i].r].vx)); } bool bin(int x,int x1,int y,int y1,int a,int a1,int b,int b1){ return (x<=a&&a1<=x1&&y<=b&&b1<=y1);} bool bou(int x,int x1,int y,int y1,int a,int a1,int b,int b1){ return (x>a1||x1<a||y>b1||y1<b);} void ask(int x,int x1,int x2,int y1,int y2){ if (!x||tr[x].vx<=mx) return; if (bin(x1,x2,y1,y2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1])){ mx=max(mx,tr[x].vx);return; }if (bou(x1,x2,y1,y2,tr[x].mn[0],tr[x].mx[0],tr[x].mn[1],tr[x].mx[1])) return; if (bin(x1,x2,y1,y2,tr[x][0],tr[x][0],tr[x][1],tr[x][1])) mx=max(mx,tr[x].v); int l,r;l=tr[x].l;r=tr[x].r; if (tr[l].vx>mx) ask(l,x1,x2,y1,y2); if (tr[r].vx>mx) ask(r,x1,x2,y1,y2); } int main(){ int n,m,i,ans=0,p,x;n=in();m=in(); for (i=1;i<=n;++i) bx[i]=bn[i]=ai[i]=in(); tr[0]=(use){0,0,0,0,0,0,0,0,0,0,0,0}; for (i=1;i<=m;++i){ p=in();x=in(); bx[p]=max(bx[p],x); bn[p]=min(bn[p],x); }for (i=1;i<=n;++i) ci[i]=(use){ai[i],bx[i],ai[i],bx[i],ai[i],bx[i],0,0,0,0,0,i}; rt=build(1,n,0); for (i=1;i<=n;++i) gi[tr[i].p]=i; for (i=1;i<=n;++i){ mx=0;ask(rt,0,bn[i],0,ai[i]); tch(gi[i],mx+1);ans=max(ans,mx+1); }printf("%d\n",ans); }