树套树
跟随着潮流,弱弱的学习了一下主席树。明白了思想之后,第一次比较快的自己写出了代码。小专题
cogs930找第K小的数||1534K大数
题目大意:静态区间第K小的查询。
思路:裸裸的主席树模板题。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct Node{ Node *ch[2]; int a,siz; Node (){ch[0]=ch[1]=NULL;siz=a=0;} void updata(){ siz=a; if (ch[0]!=NULL) siz+=ch[0]->siz; if (ch[1]!=NULL) siz+=ch[1]->siz; } }*null=new Node(),*root[100001]={NULL},q[2000001]; int a[100001]={0},a2[100001]={0},q_size=0; void insert(Node *&y,Node *&x,int l,int r,int xx) { int mid; if (x==NULL) x=null; y=&q[++q_size]; *y=Node(); if (l==r) { *y=*x; y->siz++;y->a++; return; } mid=(l+r)/2; if (xx<=a2[mid]) { insert(y->ch[0],x->ch[0],l,mid,xx); y->ch[1]=x->ch[1]; y->updata(); } else { insert(y->ch[1],x->ch[1],mid+1,r,xx); y->ch[0]=x->ch[0]; y->updata(); } } void find(Node *&x2,Node *&x1,int l,int r,int k) { int ss=0,mid; if (x2==NULL) x2=null; if (x1==NULL) x1=null; if (l==r) { printf("%d\n",a2[l]); return; } mid=(l+r)/2; if (x2->ch[0]!=NULL) ss+=x2->ch[0]->siz; if (x1->ch[0]!=NULL) ss-=x1->ch[0]->siz; if (ss>=k) find(x2->ch[0],x1->ch[0],l,mid,k); else find(x2->ch[1],x1->ch[1],mid+1,r,k-ss); } int main() { freopen("kth.in","r",stdin); freopen("kth.out","w",stdout); int n,m,i,j,k,l,r; null->ch[0]=null;null->ch[1]=null; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&a[i]); a2[i]=a[i]; } sort(a2+1,a2+n+1); int size; size=unique(a2+1,a2+n+1)-a2-1; for (i=1;i<=n;++i) insert(root[i],root[i-1],1,size,a[i]); for (i=1;i<=m;++i) { scanf("%d%d%d",&l,&r,&k); find(root[r],root[l-1],1,size,k); } fclose(stdin); fclose(stdout); }
cogs257动态排名系统
题目大意:动态区间第K小的查询
思路:裸裸的动态主席。。。在原来的主席树外面套一个树状数组,然后就可以了。
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<iostream> #include<cstring> using namespace std; struct use{ int l,r,siz; }tree[10000001]={0}; struct use1{ int xx,yy,kk; }qq[10001]={0}; int root[50001]={0},a[50001]={0},a2[60001]={0},q1[2000]={0},q2[2000]={0},tot=0,size,n; char ch[10001]; int lowbit(int x){return x&-x;} void build(int &x,int l,int r) { int mid; tree[x=++tot].siz=0; if (l==r) return; mid=(l+r)/2; build(tree[x].l,l,mid); build(tree[x].r,mid+1,r); } void ins(int last,int &i,int l,int r,int x,int flag) { tree[i=++tot].siz=tree[last].siz+flag; tree[i].l=tree[last].l;tree[i].r=tree[last].r; if (l==r) return; int mid=(l+r)/2; if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag); else ins(tree[last].r,tree[i].r,mid+1,r,x,flag); } void bit_ins(int i,int x,int flag) { for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,size,x,flag); } int ask(int l,int r,int k) { int ss=0,i,j,mid; if (l==r) return l; for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz; for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz; mid=(l+r)/2; if (ss>=k) { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l; return ask(l,mid,k); } else { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r; return ask(mid+1,r,k-ss); } } int bit_ask(int l,int r,int k) { q1[0]=q2[0]=0; for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l]; for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r]; return ask(1,size,k); } int main() { freopen("dynrank.in","r",stdin); freopen("dynrank.out","w",stdout); int m,i,j,d; scanf("%d",&d); while(d) { scanf("%d%d",&n,&m); tot=0;a2[0]=0; memset(tree,0,sizeof(tree)); memset(root,0,sizeof(root)); for (i=1;i<=n;++i) { scanf("%d",&a[i]); ++a2[0];a2[a2[0]]=a[i]; } for (i=1;i<=m;++i) { scanf("%*c%c%d%d",&ch[i],&qq[i].xx,&qq[i].yy); if (ch[i]=='Q') scanf("%d",&qq[i].kk); else a2[++a2[0]]=qq[i].yy; } sort(a2+1,a2+a2[0]+1); size=unique(a2+1,a2+a2[0]+1)-a2-1; build(root[0],1,size); for (i=1;i<=n;++i) { a[i]=upper_bound(a2+1,a2+size+1,a[i])-a2-1; bit_ins(i,a[i],1); } for (i=1;i<=m;++i) { if (ch[i]=='Q') { j=bit_ask(qq[i].xx-1,qq[i].yy,qq[i].kk); printf("%d\n",a2[j]); } else { bit_ins(qq[i].xx,a[qq[i].xx],-1); a[qq[i].xx]=upper_bound(a2+1,a2+size+1,qq[i].yy)-a2-1; bit_ins(qq[i].xx,a[qq[i].xx],1); } } --d; } fclose(stdin); fclose(stdout); }
cogs1715||bzoj3295动态逆序对
题目大意:每次删除一个数,输出删除前逆序对的总个数。
思路:用的动态主席,倒着来做,ask略有不同,然后很开心的re了,之后NU刷cogs。。。MLE了一片。。。
#include<iostream> #include<cstdio> using namespace std; struct use{ int l,r,siz; }tree[10000000]; int root[100001]={0},a[100001]={0},num[100001]={0},a2[100001]={0},del[50001]={0},q1[2000]={0},q2[2000]={0}, cc[100001]={0},tot=0,n; long long ansi[50001]={0}; int lowbit(int x) {return x&(-x);} int work(int l,int r) { int mid,ans=0,i,j; if (l==r) return ans; mid=(l+r)/2; ans+=work(l,mid)+work(mid+1,r); cc[0]=0;i=l;j=mid+1; while(i<=mid&&j<=r) { if (a2[i]<a2[j]) { cc[++cc[0]]=a2[i];++i; } else { cc[++cc[0]]=a2[j];++j; ans+=mid-i+1; } } for (;i<=mid;++i) cc[++cc[0]]=a2[i]; for (;j<=r;++j) cc[++cc[0]]=a2[j]; for (i=l;i<=r;++i) a2[i]=cc[i-l+1]; return ans; } void build(int &x,int l,int r) { int mid; tree[x=++tot].siz=0; if (l==r) return; mid=(l+r)/2; build(tree[x].l,l,mid); build(tree[x].r,mid+1,r); } void ins(int last,int &i,int l,int r,int x,int flag) { int mid; tree[i=++tot].siz=tree[last].siz+flag; tree[i].l=tree[last].l;tree[i].r=tree[last].r; if (l==r) return; mid=(l+r)/2; if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag); else ins(tree[last].r,tree[i].r,mid+1,r,x,flag); } void bit_ins(int i,int x,int flag) { for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,n,x,flag); } long long ask(int l,int r,int x,int flag) { long long ss=0; int mid,i; if (l==r) return 0; mid=(l+r)/2; if (flag==0) { ss=0; for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz; for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz; if (x<=mid) { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l; return ask(l,mid,x,flag); } else { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r; return ss+ask(mid+1,r,x,flag); } } else { ss=0; for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].r].siz; for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].r].siz; if (x<=mid) { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l; return ss+ask(l,mid,x,flag); } else { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r; return ask(mid+1,r,x,flag); } } } long long bit_ask(int l,int r,int x,int flag) { q1[0]=q2[0]=0; for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l]; for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r]; return ask(1,n,x,flag); } int main() { freopen("inverse.in","r",stdin); freopen("inverse.out","w",stdout); int m,i,j,k; long long ans=0; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&a[i]); num[a[i]]=i; } for (i=m;i>=1;--i) { scanf("%d",&del[i]); a[num[del[i]]]=0; } build(root[0],1,n); for (i=1;i<=n;++i) if (a[i]) { a2[++a2[0]]=a[i]; bit_ins(i,a[i],1); } ans=work(1,a2[0]); for (i=1;i<=m;++i) { k=del[i];j=num[k];a[j]=k; bit_ins(j,k,1); if (j>1) ans+=bit_ask(0,j-1,k,1); if (j<n) ans+=bit_ask(j,n,k,0); ansi[i]=ans; } for (i=m;i>=1;--i) printf("%I64d\n",ansi[i]); fclose(stdin); fclose(stdout); }
终于发现re的原因了,这题中每次插入一个点的时候都会新建很多节点,其实有的节点是可以直接用原来的就可以了。
#include<iostream> #include<cstdio> using namespace std; struct use{ int l,r,siz; }tree[10000000]; int root[100001]={0},a[100001]={0},num[100001]={0},a2[100001]={0},del[50001]={0},q1[2000]={0},q2[2000]={0}, cc[100001]={0},tot=0,n; long long ansi[50001]={0}; int lowbit(int x) {return x&(-x);} int work(int l,int r) { int mid,ans=0,i,j; if (l==r) return ans; mid=(l+r)/2; ans+=work(l,mid)+work(mid+1,r); cc[0]=0;i=l;j=mid+1; while(i<=mid&&j<=r) { if (a2[i]<a2[j]) { cc[++cc[0]]=a2[i];++i; } else { cc[++cc[0]]=a2[j];++j; ans+=mid-i+1; } } for (;i<=mid;++i) cc[++cc[0]]=a2[i]; for (;j<=r;++j) cc[++cc[0]]=a2[j]; for (i=l;i<=r;++i) a2[i]=cc[i-l+1]; return ans; } void build(int &x,int l,int r) { int mid; tree[x=++tot].siz=0; if (l==r) return; mid=(l+r)/2; build(tree[x].l,l,mid); build(tree[x].r,mid+1,r); } void ins(int last,int &i,int l,int r,int x,int flag) { int mid; if (!i) i=++tot; tree[i].siz=tree[last].siz+flag; tree[i].l=tree[last].l;tree[i].r=tree[last].r; if (l==r) return; mid=(l+r)/2; if (x<=mid) ins(tree[last].l,tree[i].l,l,mid,x,flag); else ins(tree[last].r,tree[i].r,mid+1,r,x,flag); } void bit_ins(int i,int x,int flag) { for (;i<=n;i+=lowbit(i)) ins(root[i],root[i],1,n,x,flag); } long long ask(int l,int r,int x,int flag) { long long ss=0; int mid,i; if (l==r) return 0; mid=(l+r)/2; if (flag==0) { ss=0; for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].l].siz; for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].l].siz; if (x<=mid) { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l; return ask(l,mid,x,flag); } else { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r; return ss+ask(mid+1,r,x,flag); } } else { ss=0; for (i=1;i<=q2[0];++i) ss+=tree[tree[q2[i]].r].siz; for (i=1;i<=q1[0];++i) ss-=tree[tree[q1[i]].r].siz; if (x<=mid) { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].l; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].l; return ss+ask(l,mid,x,flag); } else { for (i=1;i<=q2[0];++i) q2[i]=tree[q2[i]].r; for (i=1;i<=q1[0];++i) q1[i]=tree[q1[i]].r; return ask(mid+1,r,x,flag); } } } long long bit_ask(int l,int r,int x,int flag) { q1[0]=q2[0]=0; for (;l;l-=lowbit(l)) q1[++q1[0]]=root[l]; for (;r;r-=lowbit(r)) q2[++q2[0]]=root[r]; return ask(1,n,x,flag); } int main() { freopen("inverse.in","r",stdin); freopen("inverse.out","w",stdout); int m,i,j,k; long long ans=0; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&a[i]); num[a[i]]=i; } for (i=m;i>=1;--i) { scanf("%d",&del[i]); a[num[del[i]]]=0; } build(root[0],1,n); for (i=1;i<=n;++i) if (a[i]) { a2[++a2[0]]=a[i]; bit_ins(i,a[i],1); } ans=work(1,a2[0]); for (i=1;i<=m;++i) { k=del[i];j=num[k];a[j]=k; bit_ins(j,k,1); if (j>1) ans+=bit_ask(0,j-1,k,1); if (j<n) ans+=bit_ask(j,n,k,0); ansi[i]=ans; } for (i=m;i>=1;--i) printf("%lld\n",ansi[i]); fclose(stdin); fclose(stdout); }
bzoj2588 Count on a tree
题目大意:求树上某链上的第k小。
思路:dfs序(入+1、出-1),建立静态主席树,查询。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 5000005 #define M 200005 #define up 20 using namespace std; struct use{int ls,rs,v;}tr[N]; int ti[M][2]={0},tot,point[M]={0},next[M],en[M],va[M],bi[M],ki[M][2],root[M]={0},tt=0, fa[M][up]={0},dep[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 de){ int i,j,v;ti[u][0]=++tot; dep[u]=de;fa[u][0]=ff; 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,de+1); }ti[u][1]=++tot; } int lca(int u,int v){ int i,j;if (dep[u]<dep[v]) swap(u,v); 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 ins(int &i,int la,int l,int r,int x,int y){ tr[i=++tt]=tr[la];tr[i].v+=y; if (l==r) return;int mid=l+r>>1; if (x<=mid) ins(tr[i].ls=0,tr[la].ls,l,mid,x,y); else ins(tr[i].rs=0,tr[la].rs,mid+1,r,x,y); } int ask(int a,int b,int c,int d,int l,int r,int k){ if (l==r) return l; int mid=l+r>>1; int cc=tr[tr[a].ls].v+tr[tr[b].ls].v-tr[tr[c].ls].v-tr[tr[d].ls].v; if (cc<k) return ask(tr[a].rs,tr[b].rs,tr[c].rs,tr[d].rs,mid+1,r,k-cc); else return ask(tr[a].ls,tr[b].ls,tr[c].ls,tr[d].ls,l,mid,k); } int main(){ int n,m,i,j,u,v,siz,la=0,k;scanf("%d%d",&n,&m); for (i=1;i<=n;++i){scanf("%d",&va[i]);bi[i]=va[i];} sort(bi+1,bi+n+1);siz=unique(bi+1,bi+n+1)-bi-1; for (i=1;i<=n;++i) va[i]=upper_bound(bi+1,bi+siz+1,va[i])-bi-1; for (tot=0,i=1;i<n;++i){scanf("%d%d",&u,&v);add(u,v);} dfs(1,tot=0,1); for (i=1;i<=n;++i){ ki[ti[i][0]][0]=i;ki[ti[i][0]][1]=1; ki[ti[i][1]][0]=i;ki[ti[i][1]][1]=-1; }for (i=1;i<=tot;++i) ins(root[i],root[i-1],1,siz,va[ki[i][0]],ki[i][1]); for (i=1;i<=m;++i){ scanf("%d%d%d",&u,&v,&k);u^=la;j=lca(u,v); printf("%d",la=bi[ask(root[ti[u][0]],root[ti[v][0]],root[ti[j][0]], root[ti[j][0]-1],1,siz,k)]); if (i<m) printf("\n"); } }
bzoj3551 Peaks加强版
题目大意:给定一个无向图,q组询问,求从某点出发经过边权不超过v能到的点中,权值第k大的权值。
思路:肯定从最小生成树上找边。如果是离线的话,启发式合并。因为强制在线,所以可以考虑将并查集合并的时候在一个块的点能一起查询。所以可以在合并的时候,fa[r1]=r2的同时,给r2向r1连边,这样的树形结构保证了边权从上到下递减,dfs序后,能走的区间在一起,可以从一个点向上倍增到不能走,然后从这个点的儿子中二分出能走的区间(这些区间的dfs序是连续的,因为并查集的时候边权是有序的),在静态主席树上查询就可以了。
(听说还有一种叫做kruskal重构树的东西,就是在x、y合并的时候,新建一个节点t作为x、y的父亲,这样的树是二叉树,叶子节点是原来的点,非叶子节点的点权表示的是一些边权,两个叶子节点lca的权值就是两个点在原树路径上的最大路径权值。)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 500005 #define up 20 #define nn 2000005 #define inf 2100000000 using namespace std; struct use{ int u,v,va; bool operator<(const use&a)const{return va<a.va;} }ed[M]; struct tree{int l,r,sz;}tr[nn]={0}; int fa[N][up],gi[N],fa1[N],root[N]={0},hi[N],bi[N],tt=0,dl[N],dr[N],dui[N], point[N]={0},next[N],en[N],va[N],tot=0,zh[N],zl[N],zr[N],zt=0,bz,po1[N]={0},ne1[N]; int find(int x){ if (fa1[x]!=x) fa1[x]=find(fa1[x]); return fa1[x];} void add(int u,int v,int vv){next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;} int cmp(int x,int y){return gi[x]<gi[y];} void dfs(int u,int ff){ int i,v;fa[u][0]=ff; if (!ff) gi[u]=inf; dl[u]=++tt;dui[tt]=u; for (i=1;i<up;++i) fa[u][i]=fa[fa[u][i-1]][i-1]; for (i=point[u];i;i=next[i]){ ne1[i]=po1[u];po1[u]=i; }zl[u]=zt+1; for (i=po1[u];i;i=ne1[i]){ if ((v=en[i])==ff) continue; zh[++zt]=v; }zr[u]=zt; for (i=po1[u];i;i=ne1[i]){ if ((v=en[i])==ff) continue; gi[v]=va[i];dfs(v,u); }dr[u]=tt; } void ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x); } int ask(int li,int ri,int l,int r,int x){ if (l==r) return l; int ls=tr[tr[ri].l].sz-tr[tr[li].l].sz; int mid=(l+r)>>1; if (ls>=x) return ask(tr[li].l,tr[ri].l,l,mid,x); else return ask(tr[li].r,tr[ri].r,mid+1,r,x-ls); } int work(int x,int v,int k){ int rt,i,l,r,mid,ll,rr;rt=x; for (i=up-1;i>=0;--i) if (gi[fa[rt][i]]<=v) rt=fa[rt][i]; if (gi[rt]<=v) rt=fa[rt][0]; l=zl[rt];r=zr[rt]; if (l<=r){ rr=0; while(l<=r){ mid=(l+r)>>1; if (gi[zh[mid]]<=v){l=mid+1;rr=mid;} else r=mid-1; }ll=dl[rt]; if (rr) rr=dr[zh[rr]]; else rr=dl[rt]; }else{ll=rr=dl[rt];} k=rr-ll+1-k+1; if (k<=0||k>rr-ll+1) return 0; else return ask(root[ll-1],root[rr],1,bz,k); } int main(){ int n,m,q,i,la=0,r1,r2,x,v,k; scanf("%d%d%d",&n,&m,&q); for (i=1;i<=n;++i){ scanf("%d",&hi[i]); bi[i]=hi[i];fa1[i]=i; }sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1; for (i=1;i<=n;++i) hi[i]=upper_bound(bi+1,bi+bz+1,hi[i])-bi-1; for (i=1;i<=m;++i) scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].va); sort(ed+1,ed+m+1); for (i=1;i<=m;++i){ r1=find(ed[i].u);r2=find(ed[i].v); if (r1!=r2){fa1[r1]=r2;add(r2,r1,ed[i].va);} }for (i=1;i<=n;++i){ r1=find(i); if (!dl[r1]) dfs(r1,0); }for (tt=0,i=1;i<=n;++i) ins(root[i-1],root[i],1,bz,hi[dui[i]]); gi[0]=inf;bi[0]=-1; for (i=1;i<=q;++i){ scanf("%d%d%d",&x,&v,&k); if (la!=-1){x^=la;v^=la;k^=la;} la=work(x,v,k); printf("%d\n",bi[la]); la=bi[la]; } }
bzoj4299||bzoj4408
题目大意:对于一个可重集,定义它的fs为最小的可重集子集和不能表示的数。给定一个数组ai,求[l,r]这个集合的fs。
思路:对于一个有序的可重集,如果前i个数能表示到mx,新来一个数x,如果x<=mx+1,则能表示到mx+x。所以我们可以对序列建立主席树,每次查询区间内到mx+1的和,如果这个和<=mx就说明答案是mx+1,否则mx=sm。这个过程每次最少扩大两倍,所以查询的次数是logn的,总复杂度O(nlog^2n)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 4000000 #define inf 1000000010 using namespace std; struct use{int l,r,sm;}tr[M]; int rt[N]={0},tt=0; void ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];tr[i].sm+=x; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x);} int ask(int i,int j,int l,int r,int ll,int rr){ if (!(tr[j].sm-tr[i].sm)) return 0; if (ll<=l&&r<=rr) return tr[j].sm-tr[i].sm; int mid=(l+r)>>1;int sum=0; if (ll<=mid) sum+=ask(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sum+=ask(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sum;} int main(){ int n,i,j,l,r,m,ans,sm;scanf("%d",&n); memset(tr,0,sizeof(tr)); for (i=1;i<=n;++i){ scanf("%d",&j);ins(rt[i-1],rt[i],1,inf,j); }scanf("%d",&m); for (i=1;i<=m;++i){ scanf("%d%d",&l,&r); for(ans=0;;){ sm=ask(rt[l-1],rt[r],1,inf,1,ans+1); if (sm<=ans) break; ans=sm; }printf("%d\n",ans+1); } }
bzoj3207 花神的嘲讽计划
题目大意:给定一个数列ai,m个询问,问ai中x~y有没有长度为k的给定串。
思路:因为所有m的k一样,所以可以对每个位置和之前共k为的hash值用主席树记录,查询的时候查相应区间有没有hash值。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 5000005 #define ULL unsigned long long #define p 467LL using namespace std; struct use{int l,r,sz;}tr[M]; int rt[N],ai[N]={0},tt=0; ULL bi[N]={0},ci[N],mi[N]; void ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x); } int ask(int i,int j,int l,int r,int x){ if (!(tr[j].sz-tr[i].sz)) return 0; if (l==r) return tr[j].sz-tr[i].sz; int mid=(l+r)>>1; if (x<=mid) return ask(tr[i].l,tr[j].l,l,mid,x); else return ask(tr[i].r,tr[j].r,mid+1,r,x); } int main(){ int n,m,k,i,j,x,y,xx,bz;ULL cc; memset(tr,0,sizeof(tr)); scanf("%d%d%d",&n,&m,&k); for (mi[0]=1LL,i=1;i<=k;++i) mi[i]=mi[i-1]*p; for (bi[0]=0LL,i=1;i<=n;++i){ scanf("%d",&ai[i]); if (i>=k){ for (j=0;j<k;++j) bi[i]+=(ULL)ai[i-k+1+j]*mi[j]; ci[i]=bi[i]; } }sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1; for (i=k;i<=n;++i){ ai[i]=upper_bound(bi+1,bi+bz+1,ci[i])-bi-1; ins(rt[i-1],rt[i],1,bz,ai[i]); }for (i=1;i<=m;++i){ scanf("%d%d",&x,&y); for (cc=0LL,j=0;j<k;++j){ scanf("%d",&xx); cc+=(ULL)xx*mi[j]; }if (y-x+1<k){printf("Yes\n");continue;} j=upper_bound(bi+1,bi+bz+1,cc)-bi-1; if (bi[j]!=cc){printf("Yes\n");continue;} if (ask(rt[x+k-2],rt[y],1,bz,j)) printf("No\n"); else printf("Yes\n"); } }
bzoj1926 粟粟的书架
题目大意:给定一个r*c的矩阵,m个询问,每次询问一个矩阵内最少几个数的和>=h。
思路:数据分为两部分:1)r,c<=200,m<=200000。考虑建二维主席树,如果四个区间的话儿子信息比较混乱,所以可以暴力插最后一列或一行(可以选较小的那个插),查询四个区间,查询的时候尽量选右区间;2)r=1,c<=500000,m<=20000。直接主席树。
1)也可以用二分做。二分最小数+二分选最小数的个数。(区间里的数<=1000)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 15000005 #define M 1000005 #define nn 205 using namespace std; struct use{int l,r,sz,sm;}tr[N]={0}; int r1[nn][nn]={0},r2[M]={0},tt=0,bz=0,p1[nn][nn],p2[M],bi[M],s1[nn][nn]={0},s2[M]={0}; void ins(int la,int &i,int l,int r,int x){ if (i==la||!i) tr[i=++tt]=tr[la]; ++tr[i].sz;tr[i].sm+=bi[x]; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x);} int ask1(int a,int b,int c,int d,int l,int r,int x){ if (l==r) return (x+bi[l]-1)/bi[l]; int mid=(l+r)>>1;int cc=tr[tr[a].r].sm-tr[tr[b].r].sm-tr[tr[c].r].sm+tr[tr[d].r].sm; if (cc>=x) return ask1(tr[a].r,tr[b].r,tr[c].r,tr[d].r,mid+1,r,x); else return tr[tr[a].r].sz-tr[tr[b].r].sz-tr[tr[c].r].sz+tr[tr[d].r].sz+ ask1(tr[a].l,tr[b].l,tr[c].l,tr[d].l,l,mid,x-cc);} int ask2(int i,int j,int l,int r,int x){ if (l==r) return (x+bi[l]-1)/bi[l]; int mid=(l+r)>>1;int cc=tr[tr[j].r].sm-tr[tr[i].r].sm; if (cc>=x) return ask2(tr[i].r,tr[j].r,mid+1,r,x); else return tr[tr[j].r].sz-tr[tr[i].r].sz+ask2(tr[i].l,tr[j].l,l,mid,x-cc);} int main(){ int r,c,m,i,j,k,x1,x2,y1,y2,hi; scanf("%d%d%d",&r,&c,&m); if (r!=1){ for (i=1;i<=r;++i) for (j=1;j<=c;++j){ scanf("%d",&p1[i][j]);bi[++bz]=p1[i][j]; s1[i][j]=p1[i][j]+s1[i-1][j]+s1[i][j-1]-s1[i-1][j-1]; }sort(bi+1,bi+bz+1); bz=unique(bi+1,bi+bz+1)-bi-1; for (i=1;i<=r;++i) for (j=1;j<=c;++j){ p1[i][j]=upper_bound(bi+1,bi+bz+1,p1[i][j])-bi-1; if (i<j){ r1[i][j]=r1[i][j-1]; for (k=1;k<=i;++k) ins(r1[i][j-1],r1[i][j],1,bz,p1[k][j]); }else{ r1[i][j]=r1[i-1][j]; for (k=1;k<=j;++k) ins(r1[i-1][j],r1[i][j],1,bz,p1[i][k]); } } for (i=1;i<=m;++i){ scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&hi); if (s1[x2][y2]-s1[x2][y1-1]-s1[x1-1][y2]+s1[x1-1][y1-1]<hi) printf("Poor QLW\n"); else printf("%d\n",ask1(r1[x1-1][y1-1],r1[x2][y1-1], r1[x1-1][y2],r1[x2][y2],1,bz,hi)); } }else{ for (i=1;i<=c;++i){ scanf("%d",&p2[i]); bi[++bz]=p2[i]; s2[i]=s2[i-1]+p2[i]; }sort(bi+1,bi+bz+1); bz=unique(bi+1,bi+bz+1)-bi-1; for (i=1;i<=c;++i){ p2[i]=upper_bound(bi+1,bi+bz+1,p2[i])-bi-1; ins(r2[i-1],r2[i],1,bz,p2[i]); }for (i=1;i<=m;++i){ scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&hi); if (s2[y2]-s2[y1-1]<hi) printf("Poor QLW\n"); else printf("%d\n",ask2(r2[y1-1],r2[y2],1,bz,hi)); } } }
bzoj3123 森林
题目大意:森林中有点权,支持在线操作:(1)查询x到y的路径上第k大;(2)连接x和y。
思路:连接的时候相当于把以v为根的子树整个作为u的儿子,所以对于v子树外的点是没有影响的,所以可以直接用rt[u]更新rt[v](!!!),dfs着把以v为根的子树更新过去。因为只会给一些点儿子,所以可以更新这些儿子的fa。查询的时候用四段在主席树上查一下。
注意:求树上链的信息不需要求出dfs序,直接用rt[fa]更新rt[u]就可以了(!)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 #define M 20000000 #define up 20 using namespace std; struct use{int l,r,sz;}tr[M]; int ai[N],bi[N],bz,rt[N],fr[N],la[N],fa[N][up],point[N]={0},next[N],en[N],tot=0,n,tt=0, dep[N],anc[N],siz[N]; bool vi[N]={false}; int in(){ char ch=getchar(); while(ch<'A'||ch>'Z') ch=getchar(); return (ch=='Q');} 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 ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x);} int ask(int a,int b,int c,int d,int l,int r,int x){ if (l==r) return l; int mid=(l+r)>>1;int cc=tr[tr[a].l].sz+tr[tr[b].l].sz-tr[tr[c].l].sz-tr[tr[d].l].sz; if (cc>=x) return ask(tr[a].l,tr[b].l,tr[c].l,tr[d].l,l,mid,x); else return ask(tr[a].r,tr[b].r,tr[c].r,tr[d].r,mid+1,r,x-cc);} 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 dfs(int u,int ff,int an){ int i,v;fa[u][0]=ff;vi[u]=true; ins(rt[ff],rt[u],1,bz,ai[u]); anc[u]=an;++siz[an];dep[u]=dep[ff]+1; 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) dfs(v,u,an);} int calc(int u,int v,int k){ int x,l;l=lca(u,v); x=ask(rt[u],rt[v],rt[l],rt[fa[l][0]],1,bz,k); return bi[x];} void merge(int u,int v){ if (siz[anc[u]]<=siz[anc[v]]) swap(u,v); dfs(v,u,anc[u]);} int main(){ int m,i,j,t,u,v,k,op,la=0;scanf("%d",&i); scanf("%d%d%d",&n,&m,&t); for (i=1;i<=n;++i){ scanf("%d",&ai[i]); bi[i]=ai[i]; }sort(bi+1,bi+n+1); bz=unique(bi+1,bi+n+1)-bi-1; for (i=1;i<=n;++i) ai[i]=upper_bound(bi+1,bi+bz+1,ai[i])-bi-1; for (i=1;i<=m;++i){scanf("%d%d",&u,&v);add(u,v);} for (i=1;i<=n;++i) if (!vi[i]) dfs(i,0,i); for (i=1;i<=t;++i){ op=in();scanf("%d%d",&u,&v); u^=la;v^=la; if (op){ scanf("%d",&k);k^=la; printf("%d\n",la=calc(u,v,k)); }else{merge(u,v);add(u,v);} } }
bzoj4504 K个串
题目大意:给出一个序列,问这个序列子序列和第k大是多少(子序列中一种数字只算一遍)。
思路:类似超级钢琴,想到要用堆维护以某个点结尾的子序列最大,同时要记录这个最大值取的范围和位置,为了求这些,建立主席树,每种数字只有离这个点最近的那个位置有数,每次查询相当于询问以的区间的右边连续最大和(没有数的位置相当于0,所以如果不存在一个区间,相当于这个区间都是0,所以最大值的位置可以取区间内任意一个位置)。
注意:每次查询一个区间的右端连续最大和,还要加上这个区间右端点到这个子序列结尾的和才是这一段的答案。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define N 100005 #define M 4000000 #define LL long long using namespace std; struct use{LL rm,sm;int l,r,mp;}tr[M]; struct uu{ int rd,l,r,mp;LL mx; bool operator<(const uu&x)const{return mx<x.mx;} }; priority_queue<uu> st; int rt[N],tt=0,ai[N],ci[N],cz=0,la[N]={0}; 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;} use updata(use x,use y,int r){ use c;c.sm=x.sm+y.sm; c.rm=y.rm;c.mp=y.mp; if (y.sm+x.rm>c.rm||!y.mp){ c.rm=y.sm+x.rm;c.mp=x.mp; }if (!c.mp) c.mp=r; return c;} void ins(int &i,int la,int l,int r,int x,int y){ tr[i=++tt]=tr[la]; if (l==r){ tr[i].sm+=(LL)(y*ai[x]); tr[i].rm=tr[i].sm; tr[i].mp=l; return; }int ll,rr,mid=(l+r)>>1; if (x<=mid) ins(tr[i].l,tr[la].l,l,mid,x,y); else ins(tr[i].r,tr[la].r,mid+1,r,x,y); ll=tr[i].l;rr=tr[i].r; tr[i]=updata(tr[tr[i].l],tr[tr[i].r],r); tr[i].l=ll;tr[i].r=rr;} use ask(int i,int l,int r,int ll,int rr){ if (!i) return tr[i]; if (ll<=l&&r<=rr) return tr[i]; int mid=(l+r)>>1;use x1,x2; bool f1,f2;f1=f2=false; if (ll<=mid){f1=true;x1=ask(tr[i].l,l,mid,ll,rr);} if (rr>mid){f2=true;x2=ask(tr[i].r,mid+1,r,ll,rr);} if (!f2) return x1; if (!f1) return x2; return updata(x1,x2,rr);} LL asum(int i,int l,int r,int ll,int rr){ if (!i) return 0LL; if (ll<=l&&r<=rr) return tr[i].sm; int mid=(l+r)>>1;LL sm=0LL; if (ll<=mid) sm+=asum(tr[i].l,l,mid,ll,rr); if (rr>mid) sm+=asum(tr[i].r,mid+1,r,ll,rr); return sm;} int main(){ int n,k,i,x;LL ans; use cc;uu dd; n=in();k=in(); for (i=1;i<=n;++i) ci[++cz]=ai[i]=in(); sort(ci+1,ci+cz+1); cz=unique(ci+1,ci+cz+1)-ci-1; memset(tr,0,sizeof(tr)); for (i=1;i<=n;++i){ x=upper_bound(ci+1,ci+cz+1,ai[i])-ci-1; rt[i]=rt[i-1]; if (la[x]) ins(rt[i],rt[i],1,n,la[x],-1); ins(rt[i],rt[i],1,n,i,1); la[x]=i; cc=ask(rt[i],1,n,1,i); if (cc.mp) st.push((uu){i,1,i,cc.mp,cc.rm}); }while(k--){ dd=st.top();st.pop(); ans=dd.mx; if (dd.l<dd.mp){ cc=ask(rt[dd.rd],1,n,dd.l,dd.mp-1); cc.rm+=asum(rt[dd.rd],1,n,dd.mp,dd.rd); if (cc.mp) st.push((uu){dd.rd,dd.l,dd.mp-1,cc.mp,cc.rm}); }if (dd.mp<dd.r){ cc=ask(rt[dd.rd],1,n,dd.mp+1,dd.r); if (dd.r<dd.rd) cc.rm+=asum(rt[dd.rd],1,n,dd.r+1,dd.rd); if (cc.mp) st.push((uu){dd.rd,dd.mp+1,dd.r,cc.mp,cc.rm}); } }printf("%I64d\n",ans); }
bzoj4523 路由表
题目大意:认为两个IP码一样是在转成32二进制之后前x位(已知且不同串的不同)一样。操作:1)插入一个新的IP码和x;2)查一个IP码在一段区间[l,r]内匹配改变的次数(从前往后扫,先匹配1~l-1,在l~r中统计改变次数。只有能匹配且x变长时才会改变匹配)
思路:建trie树,对每个点建主席树。查询的时候找到1~l-1中最长匹配长度mx,对于l~r中的,记下每个匹配长度(>mx)的最小下标,从后往前扫的时候记一个最小下标,如果当前的比最小下标大就不会改变。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> #define N 20000000 #define M 1000000 #define up 32 using namespace std; struct use{int ch[2];}tr[N]; struct tree{int l,r,mn;}st[N]; int tt=0,ai[up+1],cnt=0,bi[up+1],rt[N]={0},tz=0; char chin(){ char ch=getchar(); while(ch<'A'||ch>'Z') ch=getchar(); return ch;} 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;} void pre(int x,int y){for (int i=(y+1)<<3;i>(y<<3);--i){ai[i]=x&1;x>>=1;}} void tch(int &i,int la,int l,int r,int x){ st[i=++tz]=st[la];st[i].mn=min(st[i].mn,x); if (l==r) return; int mid=(l+r)>>1; if (x<=mid) tch(st[i].l,st[la].l,l,mid,x); else tch(st[i].r,st[la].r,mid+1,r,x);} int ask(int i,int l,int r,int ll,int rr){ if (!i) return N; if (ll<=l&&r<=rr) return st[i].mn; int mn=N;int mid=(l+r)>>1; if (ll<=mid) mn=min(mn,ask(st[i].l,l,mid,ll,rr)); if (rr>mid) mn=min(mn,ask(st[i].r,mid+1,r,ll,rr)); return mn;} void ins(int x){ int i,u=0,v; for (i=1;i<=x;++i){ v=tr[u].ch[ai[i]]; if (!v){ v=tr[u].ch[ai[i]]=++tt; tr[tt]=(use){0,0}; }u=v; }tch(rt[u],rt[u],1,M,++cnt);} int find(int l,int r){ int i,u=0,ans=0,mn;bi[0]=0; for (i=1;i<=up;++i){ u=tr[u].ch[ai[i]]; if (!u) break; if ((ans=ask(rt[u],1,M,l,r))<=r) bi[++bi[0]]=ans; mn=st[rt[u]].mn; if (mn<l) bi[0]=0; }for (ans=0,mn=N,i=bi[0];i;--i) if (bi[i]<mn){mn=bi[i];++ans;} return ans;} int main(){ int m,i,x,l,r;char ch; scanf("%d",&m); tr[0]=(use){0,0}; st[0]=(tree){0,0,N}; while(m--){ ch=chin(); for (i=0;i<4;++i){x=in();pre(x,i);} if (ch=='A'){x=in();ins(x);} else{ l=in();r=in(); printf("%d\n",find(l,r)); } } }
bzoj3956 Count
题目大意:定义好点对为:i<j,i=j-1||(任意i<k<j,ai[k]<ai[i]&&ai[k]<ai[j]),m组询问,问l~r之间的好点对的个数。强制在线。
思路:考虑好点对的话,最多2n个。所以可以用单调栈扫出j和之前的好点对(能和j组成好点对的至多到ai[i]>=ai[j]的第一个且比ai[j]小的都会被弹掉),用主席树维护。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 300005 #define M 12000000 using namespace std; struct use{int l,r,sz;}tr[M]; int rt[N]={0},tt=0,ai[N],zh[N],zt=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;} void ins(int &i,int la,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[i].l,tr[la].l,l,mid,x); else ins(tr[i].r,tr[la].r,mid+1,r,x);} int ask(int i,int j,int l,int r,int ll,int rr){ if (!(tr[j].sz-tr[i].sz)) return 0; if (ll<=l&&r<=rr) return tr[j].sz-tr[i].sz; int sm=0,mid=(l+r)>>1; if (ll<=mid) sm+=ask(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sm+=ask(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sm;} int main(){ int n,m,tp,i,j,l,r,la=0; memset(tr,0,sizeof(tr)); n=in();m=in();tp=in(); for (i=1;i<=n;++i) ai[i]=in(); for (i=1;i<=n;++i){ rt[i]=rt[i-1]; while(zt&&ai[zh[zt]]<ai[i]){ ins(rt[i],rt[i],1,n,zh[zt]);--zt; }if (zt) ins(rt[i],rt[i],1,n,zh[zt]); if (zt&&ai[zh[zt]]==ai[i]) --zt; zh[++zt]=i; }for (i=1;i<=m;++i){ l=in();r=in(); if (tp){l=(l+la-1)%n+1;r=(r+la-1)%n+1;} if (l>r) swap(l,r); la=ask(rt[l-1],rt[r],1,n,l,r); printf("%d\n",la); } }
bzoj4571 美味
题目大意:给定一些aj,对于每组bi和xi,求max bi^(aj+xi)。
思路:按位考虑,用主席树保存权值的话,对于高位确定的数,低位是连续的,类似trie二分的查询,看某一位的时候通过之前选的数、当前位选0/1和xi来确定一个范围,看当前位能否选0/1(!!)。O(nlog^2n)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 200005 #define nn 100000 #define up 19 #define M 4000005 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;} struct use{int sz,l,r;}tr[M]; int tt=0,rt[N]={0}; void ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x); } int query(int i,int j,int l,int r,int ll,int rr){ if (ll>r||rr<l) return 0; if (!(tr[j].sz-tr[i].sz)) return 0; if (ll<=l&&r<=rr) return tr[j].sz-tr[i].sz; int mid=(l+r)>>1;int sm=0; if (ll<=mid) sm+=query(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sm+=query(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sm;} int ask(int d,int b,int x,int l,int r,int y){ if (d<0) return 0; int a=(b>>d)&1; if (query(rt[l-1],rt[r],0,nn,y+((a^1)<<d)-x,y+((a^1)<<d)+(1<<d)-1-x)){ return ask(d-1,b,x,l,r,y+((a^1)<<d))+(1<<d); }else return ask(d-1,b,x,l,r,y+(a<<d)); } int main(){ int n,m,i,b,x,l,r;n=in();m=in(); for (i=1;i<=n;++i){ x=in(); ins(rt[i-1],rt[i],0,nn,x); }for (i=1;i<=m;++i){ b=in();x=in();l=in();r=in(); printf("%d\n",ask(up,b,x,l,r,0)); } }
noi模拟赛 线段树(!!!)
题目大意:给出n个数和m个操作,每个操作是将[l,r]都赋成[l,r]的最大值,q个询问:1)ai=j;2)问进行[L,R]操作后,ak的值。
思路:进行操作相当于取从后往前能扩展的并集的区间内的最大值。用li、ri表示从i向前取2^j个能相交扩展的操作的标号,求li[i][0]、ri[i][0]的时候,用主席树求一下左右端点处最靠后编号的编号,区间赋值的时候打永久化标记。查询的时候先找到k所在的R操作之前最后一个区间,然后用倍增数组找到左右边界,查询区间最值。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 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;} struct use{int l,r,v;}tr[M]; struct qq{int l,r;}bi[N]; int ai[N],seg[N<<2],n,m,tt=0,rt[N]={0},li[N][up],ri[N][up]; void build(int i,int l,int r){ if (l==r){seg[i]=ai[l];return;} int mid=(l+r)>>1; build(i<<1,l,mid);build(i<<1|1,mid+1,r); seg[i]=max(seg[i<<1],seg[i<<1|1]); } void sch(int i,int l,int r,int x,int y){ if (l==r){seg[i]=y;return;} int mid=(l+r)>>1; if (x<=mid) sch(i<<1,l,mid,x,y); else sch(i<<1|1,mid+1,r,x,y); seg[i]=max(seg[i<<1],seg[i<<1|1]); } int smx(int i,int l,int r,int ll,int rr){ if (ll<=l&&r<=rr) return seg[i]; int mx=0,mid=(l+r)>>1; if (ll<=mid) mx=max(mx,smx(i<<1,l,mid,ll,rr)); if (rr>mid) mx=max(mx,smx(i<<1|1,mid+1,r,ll,rr)); return mx; } int ask(int i,int l,int r,int x){ int mx=tr[i].v; if (l==r) return mx; int mid=(l+r)>>1; if (x<=mid) mx=max(mx,ask(tr[i].l,l,mid,x)); else mx=max(mx,ask(tr[i].r,mid+1,r,x)); return mx;} void tch(int &i,int j,int l,int r,int ll,int rr,int x){ tr[i=++tt]=tr[j]; if (ll<=l&&r<=rr){tr[i].v=x;return;} int mid=(l+r)>>1; if (ll<=mid) tch(tr[i].l,tr[j].l,l,mid,ll,rr,x); if (rr>mid) tch(tr[i].r,tr[j].r,mid+1,r,ll,rr,x); } int main(){ freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); int q,i,j,l,r,ll,rr,k,op;n=in();m=in();q=in(); for (i=1;i<=n;++i) ai[i]=in(); build(1,1,n); memset(tr,0,sizeof(tr)); for (i=1;i<=m;++i){ bi[i]=(qq){l=in(),r=in()}; li[i][0]=ask(rt[i-1],1,n,l); for (j=1;j<up;++j) li[i][j]=li[li[i][j-1]][j-1]; ri[i][0]=ask(rt[i-1],1,n,r); for (j=1;j<up;++j) ri[i][j]=ri[ri[i][j-1]][j-1]; tch(rt[i],rt[i-1],1,n,l,r,i); }while(q--){ if ((op=in())==1){ l=in();r=in();ai[l]=r; sch(1,1,n,l,r); }else{ l=in();r=in();k=in(); j=ask(rt[r],1,n,k); if (j>=l){ for (ll=j,i=up-1;i>=0;--i) if (li[ll][i]>=l) ll=li[ll][i]; for (rr=j,i=up-1;i>=0;--i) if (ri[rr][i]>=l) rr=ri[rr][i]; ll=bi[ll].l;rr=bi[rr].r; }else ll=rr=k; printf("%d\n",smx(1,1,n,ll,rr)); } } }
noi模拟赛 基因改造计划(!!!)
题目大意:给出一个长度为n的字符串,m组询问:[l,r]中回文串的个数。
思路:把相邻字符中加入其它字符之后先用manacher处理处每个位置能延伸的最长长度fi(包含自己这一位),如果中间是字母,个数是fi[i]/2;如果是其他字符,个数是(fi[i]+1)/2,这里+1的原因是这样保证了分母的都是偶数,可以加起来之后再/2。每个询问[l,r]相当于L=2l-1,R=2r+1的区间中(sigma(i=L~R) min(fi[i],i-L+1,R-i+1))/2,分情况讨论:1)当i-L+1<=R-i+1,也就是L<=i<=l+r时,sigma(i=L~l+r) min(fi[i],i-L+1),再分情况,就是i-fi[i]<=L时算k-l,i-fi[i]>L时算fi[i],可以用主席树(第一维编号i,第二维i-fi[i])维护区间的fi[i]、i的和与个数,统计答案;2)当i-L+1>R-i+1,也就是l+r<i<=R时,sigma(i=L~l+r) min(fi[i],R-i+1),分情况的时候根据i+fi[i]和R的关系,统计答案。
注意:取min的时候,先化式子,然后尽量分情况,固定某些区间取某些值,从而可以用数据结构或其他维护。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 300005 #define M 6000005 #define LL long long #define mid (l+r)/2 #define L l*2-1 #define R r*2+1 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;} char chin(){ char ch=getchar(); while(ch<'A'||ch>'Z') ch=getchar(); return ch;} char ss[N]; struct use{int l,r,c;LL a,b;}tl[M],tr[M]; int lz=0,rz=0,sz=0,fi[N]={0},lt[N]={0},rt[N]={0}; void mana(){ int i,mx=0,id=0; for (i=1;i<sz;++i){ if (mx>i){fi[i]=min(fi[id*2-i],mx-i);} else fi[i]=1; for (;ss[i+fi[i]]==ss[i-fi[i]];++fi[i]); if (i+fi[i]>mx){mx=i+fi[i];id=i;} } } void insl(int &i,int la,int l,int r,int x,int a,int b){ tl[i=++lz]=tl[la];++tl[i].c; tl[i].a+=a;tl[i].b+=b; if (l==r) return; if (x<=mid) insl(tl[i].l,tl[la].l,l,mid,x,a,b); else insl(tl[i].r,tl[la].r,mid+1,r,x,a,b); } void insr(int &i,int la,int l,int r,int x,int a,int b){ tr[i=++rz]=tr[la];++tr[i].c; tr[i].a+=a;tr[i].b+=b; if (l==r) return; if (x<=mid) insr(tr[i].l,tr[la].l,l,mid,x,a,b); else insr(tr[i].r,tr[la].r,mid+1,r,x,a,b); } LL ala(int i,int j,int l,int r,int ll,int rr){ if (!(tl[j].a-tl[i].a)) return 0LL; if (ll<=l&&r<=rr) return tl[j].a-tl[i].a; LL sm=0LL; if (ll<=mid) sm+=ala(tl[i].l,tl[j].l,l,mid,ll,rr); if (rr>mid) sm+=ala(tl[i].r,tl[j].r,mid+1,r,ll,rr); return sm;} LL alb(int i,int j,int l,int r,int ll,int rr){ if (!(tl[j].b-tl[i].b)) return 0LL; if (ll<=l&&r<=rr) return tl[j].b-tl[i].b; LL sm=0LL; if (ll<=mid) sm+=alb(tl[i].l,tl[j].l,l,mid,ll,rr); if (rr>mid) sm+=alb(tl[i].r,tl[j].r,mid+1,r,ll,rr); return sm;} int alc(int i,int j,int l,int r,int ll,int rr){ if (!(tl[j].c-tl[i].c)) return 0; if (ll<=l&&r<=rr) return tl[j].c-tl[i].c; int sm=0; if (ll<=mid) sm+=alc(tl[i].l,tl[j].l,l,mid,ll,rr); if (rr>mid) sm+=alc(tl[i].r,tl[j].r,mid+1,r,ll,rr); return sm;} LL ara(int i,int j,int l,int r,int ll,int rr){ if (!(tr[j].a-tr[i].a)) return 0LL; if (ll<=l&&r<=rr) return tr[j].a-tr[i].a; LL sm=0LL; if (ll<=mid) sm+=ara(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sm+=ara(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sm;} LL arb(int i,int j,int l,int r,int ll,int rr){ if (!(tr[j].b-tr[i].b)) return 0LL; if (ll<=l&&r<=rr) return tr[j].b-tr[i].b; LL sm=0LL; if (ll<=mid) sm+=arb(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sm+=arb(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sm;} int arc(int i,int j,int l,int r,int ll,int rr){ if (!(tr[j].c-tr[i].c)) return 0; if (ll<=l&&r<=rr) return tr[j].c-tr[i].c; int sm=0; if (ll<=mid) sm+=arc(tr[i].l,tr[j].l,l,mid,ll,rr); if (rr>mid) sm+=arc(tr[i].r,tr[j].r,mid+1,r,ll,rr); return sm;} int main(){ freopen("gene.in","r",stdin); freopen("gene.out","w",stdout); int n,m,i,l,r;LL ans=0LL;n=in();m=in(); ss[sz++]='B';ss[sz++]='#'; for (i=1;i<=n;++i){ ss[sz++]=chin();ss[sz++]='#'; }ss[sz]='B';mana(); for (i=1;i<sz;++i){ insl(lt[i],lt[i-1],0,sz,i-fi[i],i,fi[i]); insr(rt[i],rt[i-1],0,sz,i+fi[i],i,fi[i]); }while(m--){ l=in();r=in(); ans=ala(lt[L-1],lt[l+r],0,sz,0,L)-(LL)alc(lt[L-1],lt[l+r],0,sz,0,L)*(L-1)+ (L<sz ? alb(lt[L-1],lt[l+r],0,sz,L+1,sz) : 0LL); if (l+r<R) ans+=arb(rt[l+r],rt[R],0,sz,0,R)+(R<sz ? (LL)arc(rt[l+r],rt[R],0,sz,R+1,sz)*(R+1)- ara(rt[l+r],rt[R],0,sz,R+1,sz) : 0LL); ans-=(LL)(r-l+2); printf("%I64d\n",ans/2LL); } }
其他树套树问题
cogs1345 K大数查询
题目大意:有n个连续的区间,每个区间可以放多个数,区间修改,查找区间内的第K大。
思路:树状数组(权值)套线段树(位置)。因为不会写其他的树套树,也不会树状数组的区间修改,于是只能写成这样了。插入的时候log^2n,查询的时候二分一下log^3n,然后就会发现自己T了。因为自己用了pushdown,然后就会开很多没有用的点,也会一遍一遍的访问他们。那我们只能不用pushdown,然后传入一个变量fadel,然后更新一下就可以了!!!那么这里的siz保存的就是这个节点及它下面的元素个数,并没有保存它上方的!!!因为这里是第K大,而我只会树状数组第K小,所以又用了一棵线段树来维护区间元素的个数,以转化成第K小。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct use{ int l,r,siz,del; }tree[20000000]={0}; int tot=0,wotr[200000]={0},delta[200000]={0},root[50001]={0},n,size=0; int lowbit(int x){return x&(-x);} void paint(int i,int x,int l,int r) { delta[i]+=x;wotr[i]+=x*(r-l+1); } void pushdown(int i,int l,int r) { int mid; mid=(l+r)/2; paint(i*2,delta[i],l,mid); paint(i*2+1,delta[i],mid+1,r); delta[i]=0; } void insert(int i,int l,int r,int ll,int rr) { int mid; if (ll<=l&&r<=rr) { paint(i,1,l,r); return; } mid=(l+r)/2; pushdown(i,l,r); if (ll<=mid) insert(i*2,l,mid,ll,rr); if (rr>mid) insert(i*2+1,mid+1,r,ll,rr); wotr[i]=wotr[i*2]+wotr[i*2+1]; } int work(int i,int l,int r,int ll,int rr) { int mid,sum=0; if (ll<=l&&r<=rr) return wotr[i]; pushdown(i,l,r);mid=(l+r)/2; if (ll<=mid) sum+=work(i*2,l,mid,ll,rr); if (rr>mid) sum+=work(i*2+1,mid+1,r,ll,rr); return sum; } void ins(int &i,int l,int r,int ll,int rr,int x,int fadel) { int mid; if (i==0) i=++tot; if (ll<=l&&r<=rr) { ++tree[i].del;tree[i].siz=(tree[i].l==0?0:tree[tree[i].l].siz)+(tree[i].r==0?0:tree[tree[i].r].siz)+tree[i].del*(r-l+1); return; } mid=(l+r)/2; if (ll<=mid) ins(tree[i].l,l,mid,ll,rr,x,fadel+tree[i].del); if (rr>mid) ins(tree[i].r,mid+1,r,ll,rr,x,fadel+tree[i].del); tree[i].siz=(tree[i].l==0?0:tree[tree[i].l].siz)+(tree[i].r==0?0:tree[tree[i].r].siz)+tree[i].del*(r-l+1); } void bit_ins(int x,int l,int r) { for (;x<=n;x+=lowbit(x)) ins(root[x],1,n,l,r,1,0); } int ask(int i,int l,int r,int ll,int rr,int fadel) { int mid,sum=0; if (!i) return (fadel*(min(r,rr)-max(l,ll)+1)); if (ll<=l&&r<=rr) return tree[i].siz+fadel*(r-l+1); mid=(l+r)/2; if (ll<=mid) sum+=ask(tree[i].l,l,mid,ll,rr,tree[i].del+fadel); if (rr>mid) sum+=ask(tree[i].r,mid+1,r,ll,rr,tree[i].del+fadel); return sum; } int bit_ask(int x,int ll,int rr) { int sum=0; for (;x>0;x-=lowbit(x)) sum+=ask(root[x],1,n,ll,rr,0); return sum; } int to_ask(int l,int r,int ll,int rr,int k) { int mid,sum=0,ans; if (l==r) return l; mid=(l+r)/2; sum=bit_ask(mid,ll,rr); if (k<=sum) ans=to_ask(l,mid,ll,rr,k); else ans=to_ask(mid+1,r,ll,rr,k); return ans; } int main() { freopen("zjoi13_sequence.in","r",stdin); freopen("zjoi13_sequence.out","w",stdout); int m,i,j,kind,aa,bb,cc; scanf("%d%d",&n,&m); for (i=1;i<=m;++i) { scanf("%d%d%d%d",&kind,&aa,&bb,&cc); if (kind==1) { bit_ins(cc,aa,bb); insert(1,1,n,aa,bb); } else { j=work(1,1,n,aa,bb); cc=j-cc+1; j=to_ask(1,n,aa,bb,cc); printf("%d\n",j); } } fclose(stdin); fclose(stdout); }
bzoj1146 网络管理
题目大意:树上带修改第k大查询。
思路:链剖+线段树套平衡树。暴力建树,查询的时候先二分答案,然后链剖求小于等于它的个数。改的时候删点加点就可以了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 100000 using namespace std; struct node{ node *ch[2]; int r,v,num,siz; node(int v):v(v) {siz=num=1;r=rand();ch[0]=ch[1]=NULL;} int cmp(int x){return x==v ? -1 : x>v;} void updata() { siz=num; if (ch[0]!=NULL) siz+=ch[0]->siz; if (ch[1]!=NULL) siz+=ch[1]->siz; } }*tree[maxnode*4]; int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},ti[maxnode]={0},id[maxnode]={0},tot=0,n; void add(int u,int v) { ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v; ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u; } void rotate(node* &o,int d) { node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->updata();k->updata();o=k; } void insert(node* &o,int v) { if (o==NULL) o=new node(v); else { int d=o->cmp(v); if (d==-1) ++o->num; else { insert(o->ch[d],v); if (o->ch[d]->r > o->r) rotate(o,d^1); } } o->updata(); } void del(node* &o,int v) { int d=o->cmp(v); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0]->r > o->ch[1]->r ? 1 : 0); rotate(o,d2);del(o->ch[d2],v); } } } else -- o->num; } else del(o->ch[d],v); if (o!=NULL) o->updata(); } int kth(node *o,int k) { if ((o->ch[0]==NULL ? 0 : o->ch[0]->siz)>=k) return kth(o->ch[0],k); if ((o->ch[0]==NULL ? 0 : o->ch[0]->siz)+ o->num <k) return kth(o->ch[1],k-(o->ch[0]==NULL ? 0 : o->ch[0]->siz)- o->num); return o->v; } int rank(node *o,int k) { if (o==NULL) return 0; if (o->v==k) return (o->ch[0]==NULL ? 0 : o->ch[0]->siz) + o->num; if (o->v > k) return rank(o->ch[0],k); if (o->v < k) return rank(o->ch[1],k)+o->num+(o->ch[0]==NULL ? 0 : o->ch[0]->siz); } void inst(node* &o,int l,int r) { int i; for (i=l;i<=r;++i) insert(o,ti[id[i]]); } void build(int i,int l,int r) { int mid; inst(tree[i],l,r);if (l==r) return; mid=(l+r)/2;build(i*2,l,mid);build(i*2+1,mid+1,r); } void tch(int i,int l,int r,int x,int y) { int mid; if (l==r){tree[i]=new node(y);return;} mid=(l+r)/2; if (x<=mid) tch(i*2,l,mid,x,y); else tch(i*2+1,mid+1,r,x,y); del(tree[i],ti[id[x]]);insert(tree[i],y); } int tsum(int i,int l,int r,int ll,int rr) { int mid,sum=0; if (ll<=l&&r<=rr) return tree[i]->siz; mid=(l+r)/2; if (ll<=mid) sum+=tsum(i*2,l,mid,ll,rr); if (rr>mid) sum+=tsum(i*2+1,mid+1,r,ll,rr); return sum; } int task(int i,int l,int r,int ll,int rr,int x) { int mid,sum=0; if (ll<=l&&r<=rr) return rank(tree[i],x); mid=(l+r)/2; if (ll<=mid) sum+=task(i*2,l,mid,ll,rr,x); if (rr>mid) sum+=task(i*2+1,mid+1,r,ll,rr,x); return sum; } struct lp{ int fa[maxnode],son[maxnode],dep[maxnode],siz[maxnode],tid[maxnode],top[maxnode]; bool visit[maxnode]; void dfs1(int u,int f,int depth) { int i,j,maxsiz=0; son[u]=0;fa[u]=f;dep[u]=depth;siz[u]=1; for (i=point[u];i;i=next[i]) if (en[i]!=f) { dfs1(en[i],u,depth+1);siz[u]+=siz[en[i]]; if (siz[en[i]]>maxsiz) { son[u]=en[i];maxsiz=siz[en[i]]; } } } void dfs2(int u,int anc) { int i,j; tid[u]=++tot;id[tot]=u; top[u]=anc;visit[u]=true; if (son[u]) dfs2(son[u],anc); for (i=point[u];i;i=next[i]) if (!visit[en[i]]) dfs2(en[i],en[i]); } int qsum(int a,int b) { int sum=0; while(top[a]!=top[b]) { if (dep[top[a]]<dep[top[b]]) swap(a,b); sum+=tsum(1,1,n,tid[top[a]],tid[a]); a=fa[top[a]]; } if (dep[a]>dep[b]) swap(a,b); sum+=tsum(1,1,n,tid[a],tid[b]); return sum; } int qask(int a,int b,int k) { int sum=0; while(top[a]!=top[b]) { if (dep[top[a]]<dep[top[b]]) swap(a,b); sum+=task(1,1,n,tid[top[a]],tid[a],k); a=fa[top[a]]; } if (dep[a]>dep[b]) swap(a,b); sum+=task(1,1,n,tid[a],tid[b],k);return sum; } }t; void ask(int a,int b,int k) { int l,r,mid,sum,i; sum=t.qsum(a,b); if (k>sum){printf("invalid request!\n");return;} k=sum+1-k;l=1;r=n; while(l<r) { mid=(l+r)/2; i=kth(tree[1],mid); sum=t.qask(a,b,i); if (sum>k-1) r=mid; else l=mid+1; } printf("%d\n",kth(tree[1],l)); } int main() { int q,i,j,u,v,k; scanf("%d%d",&n,&q); for (i=1;i<=n;++i) scanf("%d",&ti[i]); for (i=1;i<n;++i){scanf("%d%d",&u,&v);add(u,v);} t.dfs1(1,0,1);tot=0;t.dfs2(1,1);build(1,1,n); for (i=1;i<=q;++i) { scanf("%d%d%d",&k,&u,&v); if (k==0) { tch(1,1,n,t.tid[u],v);ti[u]=v; } else ask(u,v,k); } }
bzoj3196 二逼平衡树
题目大意:区间第k小、排名、前驱、后继,单点修改。
思路:线段树套平衡树。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 50005 #define inf 2100000000LL using namespace std; struct node{ node *ch[2]; int r,v,num,siz; node(int v):v(v) {siz=num=1;r=rand();ch[0]=ch[1]=NULL;} int cmp(int x){return x==v ? -1 : x>v;} int lsiz(){return (ch[0]==NULL ? 0 : ch[0]->siz);} int rsiz(){return (ch[1]==NULL ? 0 : ch[1]->siz);} void updata(){siz=num+lsiz()+rsiz();} }*tree[maxnode*4]; int ai[maxnode]={0},n,aa; inline void rotate(node* &o,int d) { node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->updata();k->updata();o=k; } inline void insert(node* &o,int v) { if (o==NULL) o=new node(v); else { int d=o->cmp(v); if (d==-1) ++o->num; else { insert(o->ch[d],v); if (o->ch[d]->r > o->r) rotate(o,d^1); } } o->updata(); } inline void del(node* &o,int v) { int d=o->cmp(v); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0]->r > o->ch[1]->r ? 1 : 0); rotate(o,d2);del(o->ch[d2],v); } } } else -- o->num; } else del(o->ch[d],v); if (o!=NULL) o->updata(); } inline int rank(node* &o,int x) { if (o==NULL) return 0; int d=o->cmp(x); if (d==-1) return o->lsiz()+o->num; if (d==0) return rank(o->ch[d],x); else return rank(o->ch[d],x) + o->num + o->lsiz(); } inline int kth(node* &o,int x) { if (o->lsiz() >= x) return kth(o->ch[0],x); if (o->lsiz() + o->num >=x) return o->v; else return kth(o->ch[1],x- o->lsiz() - o->num); } inline void pre(node* &o,int x) { if (o==NULL) return; if (o->v < x){aa=max(aa,o->v);pre(o->ch[1],x);} else pre(o->ch[0],x); } inline void succ(node* &o,int x) { if (o==NULL) return; if (o->v > x){aa=min(aa,o->v);succ(o->ch[0],x);} else succ(o->ch[1],x); } inline void inst(node* &o,int l,int r) { for (int i=l;i<=r;++i) insert(o,ai[i]); } inline void build(int i,int l,int r) { int mid; inst(tree[i],l,r);if (l==r) return; mid=(l+r)/2;build(i*2,l,mid);build(i*2+1,mid+1,r); } inline int qsum(int i,int l,int r,int ll,int rr) { if (ll<=l&&r<=rr) return tree[i]->siz; int mid=(l+r)/2;int sum=0; if (ll<=mid) sum+=qsum(i*2,l,mid,ll,rr); if (rr>mid) sum+=qsum(i*2+1,mid+1,r,ll,rr); return sum; } inline void tch(int i,int l,int r,int x,int y) { if (l==r){tree[i]=new node(y);return;} int mid=(l+r)/2; if (x<=mid) tch(i*2,l,mid,x,y); else tch(i*2+1,mid+1,r,x,y); del(tree[i],ai[x]);insert(tree[i],y); } inline int qrank(int i,int l,int r,int ll,int rr,int k) { if (ll<=l&&r<=rr) return rank(tree[i],k); int mid=(l+r)/2;int sum=0; if (ll<=mid) sum+=qrank(i*2,l,mid,ll,rr,k); if (rr>mid) sum+=qrank(i*2+1,mid+1,r,ll,rr,k); return sum; } inline int qkth(int ll,int rr,int k) { int sum,l,r,mid,i; l=1;r=n;sum=rr-ll+1; while(l<r) { mid=(l+r)/2;i=kth(tree[1],mid); sum=qrank(1,1,n,ll,rr,i); if (sum>=k) r=mid; else l=mid+1; } return kth(tree[1],l); } inline int qpre(int i,int l,int r,int ll,int rr,int x) { if (ll<=l&&r<=rr){aa=-inf;pre(tree[i],x);return aa;} int mid=(l+r)/2;int ans=-inf; if (ll<=mid) ans=max(ans,qpre(i*2,l,mid,ll,rr,x)); if (rr>mid) ans=max(ans,qpre(i*2+1,mid+1,r,ll,rr,x)); return ans; } inline int qsucc(int i,int l,int r,int ll,int rr,int x) { if (ll<=l&&r<=rr){aa=inf;succ(tree[i],x);return aa;} int mid=(l+r)/2;int ans=inf; if (ll<=mid) ans=min(ans,qsucc(i*2,l,mid,ll,rr,x)); if (rr>mid) ans=min(ans,qsucc(i*2+1,mid+1,r,ll,rr,x)); return ans; } int main() { freopen("psh.in","r",stdin); freopen("psh.out","w",stdout); int m,i,j,l,r,k,opt; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&ai[i]); build(1,1,n); for (i=1;i<=m;++i) { scanf("%d",&opt); if (opt!=3) { scanf("%d%d%d",&l,&r,&k);if (l>r) swap(l,r); if (opt==1) printf("%d\n",qrank(1,1,n,l,r,k-1)+1); if (opt==2) printf("%d\n",qkth(l,r,k)); if (opt==4) printf("%d\n",qpre(1,1,n,l,r,k)); if (opt==5) printf("%d\n",qsucc(1,1,n,l,r,k)); } else{scanf("%d%d",&l,&k);tch(1,1,n,l,k);ai[l]=k;} } fclose(stdin); fclose(stdout); }
bzoj2653 middle
题目大意:给定一个数列,求左端点在[a,b]、右端点在[c,d]的子序列中位数的最大值(如果n个数从0开始编号,中位数是排序后的第n/2位数)。强制在线。
思路:二分+权值线段树套位置线段树(!!!)。二分答案x,>=x的赋成-1,<=x的赋成1,相当于求左端点[a,b]右端点[c,d]是否存在一段区间和<=0,[b,c]的情况是固定的,所以要求[a,b-1]和[c+1,d]的左或右延续最小子段和,用树套树维护。
注意:树套树中有一些节点是空的,这些节点所维护的信息在每一次updata前都要注意更新到。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 20005 #define M 1000000 using namespace std; struct uu{ int x,po; bool operator<(const uu&a)const{return x<a.x;} }ni[N]; struct use{int l,r,sz,ls,rs,len;}tr[M]={0}; int ai[N],bi[N],zh[4],bz,rt[N]={0},tt=0,n; use updata(use cc,use x,use y){ use c;c.l=cc.l;c.r=cc.r; c.sz=x.sz+y.sz; c.len=max(cc.len,x.len+y.len); c.ls=min(x.ls,x.sz-(x.len-x.sz)+y.ls); c.rs=min(y.rs,y.sz-(y.len-y.sz)+x.rs); return c;} void ins(int la,int &i,int l,int r,int x){ tr[i=++tt]=tr[la];++tr[i].sz;tr[i].len=r-l+1; if (l==r){tr[i].ls=tr[i].rs=0;return;} int mid=(l+r)>>1; if (x<=mid) ins(tr[la].l,tr[i].l,l,mid,x); else ins(tr[la].r,tr[i].r,mid+1,r,x); if (!tr[i].l){tr[tr[i].l].len=mid-l+1;tr[tr[i].l].ls=tr[tr[i].l].rs=-(mid-l+1);} if (!tr[i].r){tr[tr[i].r].len=r-mid;tr[tr[i].r].ls=tr[tr[i].r].rs=-(r-mid);} tr[i]=updata(tr[i],tr[tr[i].l],tr[tr[i].r]);} use ask(int i,int l,int r,int ll,int rr){ if (ll<=l&&r<=rr){ if (!i){tr[i].len=r-l+1;tr[i].ls=tr[i].rs=-(r-l+1);} return tr[i]; }int mid=(l+r)>>1;use x,y; bool f1,f2;f1=f2=false; if (ll<=mid){x=ask(tr[i].l,l,mid,ll,rr);f1=true;} if (rr>mid){y=ask(tr[i].r,mid+1,r,ll,rr);f2=true;} if (!f1) return y; if (!f2) return x; return updata(x,x,y);} bool judge(int x){ int ll=0,rr=0,mm;use ci; if (x==1) return true; ci=ask(rt[x-1],1,n,zh[1]+1,zh[2]+1); mm=ci.sz-(ci.len-ci.sz); if (zh[0]<zh[1]){ ci=ask(rt[x-1],1,n,zh[0]+1,zh[1]); ll=ci.rs; }if (zh[2]<zh[3]){ ci=ask(rt[x-1],1,n,zh[2]+2,zh[3]+1); rr=ci.ls; }return ll+mm+rr<=0;} int main(){ int m,i,j,l,r,mid,x;scanf("%d",&n); for (i=1;i<=n;++i){scanf("%d",&ai[i]);bi[i]=ai[i];} sort(bi+1,bi+n+1);bz=unique(bi+1,bi+n+1)-bi-1; for (i=1;i<=n;++i){ ai[i]=upper_bound(bi+1,bi+bz+1,ai[i])-bi-1; ni[i]=(uu){ai[i],i}; }sort(ni+1,ni+n+1); for (i=1;i<=n;++i){ if (i&&ni[i].x==ni[i-1].x) ins(rt[ni[i].x],rt[ni[i].x],1,n,ni[i].po); else ins(rt[ni[i].x-1],rt[ni[i].x],1,n,ni[i].po); }scanf("%d",&m); for (x=0LL,i=1;i<=m;++i){ for (j=0;j<4;++j){ scanf("%d",&zh[j]);zh[j]=(zh[j]+x)%n; }sort(zh+0,zh+4); l=1;r=bz; while(l<=r){ mid=(l+r)>>1; if (judge(mid)){l=mid+1;x=mid;} else r=mid-1; }x=bi[x];printf("%d\n",x); } }