BZOJ4538:[Hnoi2016]网络 (整体二分+Lca+树状数组/线段树+路径交/树链剖分+Heap)
2017-08-28 16:49
369 查看
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4538
题目分析:这题网上好多人写树剖啊,都是把一条路径的区间搞出来之后取反更新,删除的话就套个Heap或者写线段树CDQ分治blablabla,时间复杂度O(nlog3(n)),好像因为树剖和Heap的常数特别小所以根本不虚。网上某大神用这种方法8200ms就过了,而我写了个O(nlog2(n))的线段树+路径交稳稳地T掉了,我写的另一个O(nlog2(n))的整体二分+树状数组用了11000msQAQ。可能是出题人为了卡暴力,特地将树构造成接近链的形状,结果正中树剖下怀之类的……
其实我一开始就想的是树剖,这应该是一个比较暴力的想法:用树剖将某条路径变成DFS序上不超过log(n)段区间,然后将这log(n)段区间取反,即取补集,可以证明取反后也只有log(n)段区间。将当前路径的重要程度值加进线段树的这些区间,线段树的每个节点再维护一棵Treap,以支持删除操作(当然,Treap换成Heap常数更小)。还可以写线段树CDQ分治。对于出现时间跨越了整个当前时间段[L,R]的边,直接加进去维护最大值,用完之后打一个懒惰标记清空线段树即可。
当然,也并不是没有时间复杂度更小的方法,我们可以用整体二分。假设二分答案的区间为[L,R],其中间值为mid,那就将所有重要程度值>mid的路径上面的点权值全部+1,并记录当前存在几条路径,假设查询的时候,某个点的权值=当前存在路径数,就说明所有重要程度值>mid的路径都包含该点,就要向左边递归,否则递归右边。路径加法单点查询可以转化为单点加法子树查询,用树状数组即可做到O(nlog2(n))。
CODE(整体二分+Lca+树状数组):
还有一种做法,和整体二分的思想差不多,时间复杂度也一样(但常数根本不是一个级别,我估计下面的代码常数是2000左右)。我们将所有路径按重要程度值插入Treap里面,并且在Treap的每一个节点里维护它子树中所有路径的交集。查询一个点的时候就二叉查找,优先考虑右子树,但如果该点在右子树的所有路径的交集上,就要往左走。由于路径交是log(n)的,所以总时间是O(nlog2(n))的。用Treap的话就可以处理强制在线的情况,但由于平衡树是动态的,每一次左旋右旋都要重做一次路径交,代价很高,所以写离线加线段树会更好,然而还是各种TTTTT……
(为了安(qi)慰(pian)自己码code的时间没有白费,在此将TLE的代码也贴一贴,已通过对拍OwO)
CODE(线段树+路径交):
题目分析:这题网上好多人写树剖啊,都是把一条路径的区间搞出来之后取反更新,删除的话就套个Heap或者写线段树CDQ分治blablabla,时间复杂度O(nlog3(n)),好像因为树剖和Heap的常数特别小所以根本不虚。网上某大神用这种方法8200ms就过了,而我写了个O(nlog2(n))的线段树+路径交稳稳地T掉了,我写的另一个O(nlog2(n))的整体二分+树状数组用了11000msQAQ。可能是出题人为了卡暴力,特地将树构造成接近链的形状,结果正中树剖下怀之类的……
其实我一开始就想的是树剖,这应该是一个比较暴力的想法:用树剖将某条路径变成DFS序上不超过log(n)段区间,然后将这log(n)段区间取反,即取补集,可以证明取反后也只有log(n)段区间。将当前路径的重要程度值加进线段树的这些区间,线段树的每个节点再维护一棵Treap,以支持删除操作(当然,Treap换成Heap常数更小)。还可以写线段树CDQ分治。对于出现时间跨越了整个当前时间段[L,R]的边,直接加进去维护最大值,用完之后打一个懒惰标记清空线段树即可。
当然,也并不是没有时间复杂度更小的方法,我们可以用整体二分。假设二分答案的区间为[L,R],其中间值为mid,那就将所有重要程度值>mid的路径上面的点权值全部+1,并记录当前存在几条路径,假设查询的时候,某个点的权值=当前存在路径数,就说明所有重要程度值>mid的路径都包含该点,就要向左边递归,否则递归右边。路径加法单点查询可以转化为单点加法子树查询,用树状数组即可做到O(nlog2(n))。
CODE(整体二分+Lca+树状数组):
#include<iostream> #include<string> #include<cstring> #include<cmath> #include<cstdio> #include<cstdlib> #include<algorithm> #include<stdio.h> using namespace std; const int maxn=100100; const int maxl=20; struct edge { int obj; edge *Next; } e[maxn<<1]; edge *head[maxn]; int cur=-1; struct data { int Type,u,v,w,val; } work[maxn<<2]; int bit[maxn]; int num=0; int fa[maxn][maxl]; int dep[maxn]; int st[maxn]; int ed[maxn]; int Time=0; int a[maxn<<2]; bool vis[maxn<<2]; bool Left[maxn<<2]; int b[maxn<<2]; int n,m; void Add(int x,int y) { cur++; e[cur].obj=y; e[cur].Next=head[x]; head[x]=e+cur; } void Dfs(int node) { st[node]=++Time; for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if (son!=fa[node][0]) { fa[son][0]=node; dep[son]=dep[node]+1; Dfs(son); } } ed[node]=Time; } int Lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (int j=maxl-1; j>=0; j--) if (dep[ fa[x][j] ]>=dep[y]) x=fa[x][j]; if (x==y) return x; for (int j=maxl-1; j>=0; j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j]; return fa[x][0]; } void Update(int x,int v) { while (x<=n+1) { bit[x]+=v; x+=(x&(-x)); } } int Sum(int x) { int sum=0; while (x) { sum+=bit[x]; x-=(x&(-x)); } return sum; } void Binary(int L,int R,int x,int y) { if (L==R) { for (int i=x; i<=y; i++) { int z=a[i],k=work[z].Type; if (k==0) { num++; Update(st[ work[z].u ]+1,1); Update(st[ work[z].v ]+1,1); Update(st[ work[z].w ]+1,-1); Update(st[ fa[ work[z].w ][0] ]+1,-1); } if (k==1) { num--; z=work[z].u; Update(st[ work[z].u ]+1,-1); Update(st[ work[z].v ]+1,-1); Update(st[ work[z].w ]+1,1); Update(st[ fa[ work[z].w ][0] ]+1,1); } if (k==2) { int temp=Sum(ed[ work[z].u ]+1)-Sum(st[ work[z].u ]); if (temp<num) work[z].val=L; else work[z].val=-1; } } return; } int mid=(L+R)>>1; for (int i=x; i<=y; i++) { Left[i]=true; int z=a[i],k=work[z].Type; if ( k==0 && work[z].val>mid ) { Left[i]=false; num++; Update(st[ work[z].u ]+1,1); Update(st[ work[z].v ]+1,1); Update(st[ work[z].w ]+1,-1); Update(st[ fa[ work[z].w ][0] ]+1,-1); } if ( k==1 && work[ work[z].u ].val>mid ) { Left[i]=false; num--; z=work[z].u; Update(st[ work[z].u ]+1,-1); Update(st[ work[z].v ]+1,-1); Update(st[ work[z].w ]+1,1); Update(st[ fa[ work[z].w ][0] ]+1,1); } if (k==2) { int temp=Sum(ed[ work[z].u ]+1)-Sum(st[ work[z].u ]); if (temp<num) Left[i]=false; } } int cnt1=0; for (int i=x; i<=y; i++) if (Left[i]) b[++cnt1]=a[i]; int cnt2=cnt1; for (int i=x; i<=y; i++) if (!Left[i]) b[++cnt2]=a[i]; for (int i=x; i<=y; i++) Left[i]=false; for (int i=1; i<=cnt2; i++) a[x+i-1]=b[i]; if (cnt1) Binary(L,mid,x,x+cnt1-1); if (cnt1<cnt2) Binary(mid+1,R,x+cnt1,y); } int main() { freopen("4538.in","r",stdin); freopen("4538.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1; i<=n; i++) head[i]=NULL; for (int i=1; i<n; i++) { int x,y; scanf("%d%d",&x,&y); Add(x,y); Add(y,x); } Dfs(1); for (int j=1; j<maxl; j++) for (int i=1; i<=n; i++) fa[i][j]=fa[ fa[i][j-1] ][j-1]; for (int i=1; i<=m; i++) { scanf("%d",&work[i].Type); if (work[i].Type==0) { scanf("%d%d%d",&work[i].u,&work[i].v,&work[i].val); work[i].w=Lca(work[i].u,work[i].v); vis[i]=true; } else { scanf("%d",&work[i].u); if (work[i].Type==1) vis[ work[i].u ]=false; } } for (int i=1; i<=m; i++) if (vis[i]) work[++m].Type=1,work[m].u=i; for (int i=1; i<=m; i++) a[i]=i; Binary(1,1e9,1,m); for (int i=1; i<=m; i++) if (work[i].Type==2) printf("%d\n",work[i].val); return 0; }
还有一种做法,和整体二分的思想差不多,时间复杂度也一样(但常数根本不是一个级别,我估计下面的代码常数是2000左右)。我们将所有路径按重要程度值插入Treap里面,并且在Treap的每一个节点里维护它子树中所有路径的交集。查询一个点的时候就二叉查找,优先考虑右子树,但如果该点在右子树的所有路径的交集上,就要往左走。由于路径交是log(n)的,所以总时间是O(nlog2(n))的。用Treap的话就可以处理强制在线的情况,但由于平衡树是动态的,每一次左旋右旋都要重做一次路径交,代价很高,所以写离线加线段树会更好,然而还是各种TTTTT……
(为了安(qi)慰(pian)自己码code的时间没有白费,在此将TLE的代码也贴一贴,已通过对拍OwO)
CODE(线段树+路径交):
#include<iostream> #include<string> #include<cstring> #include<cmath> #include<cstdio> #include<cstdlib> #include<stdio.h> #include<algorithm> using namespace std; const int maxn=100100; const int maxl=20; struct Tnode { int U,V,cnt; } tree[maxn<<3]; int bot[maxn<<1]; struct edge { int obj; edge *Next; } e[maxn<<1]; edge *head[maxn]; int cur=-1; struct data { int Type,u,v,val,id,Time; } work[maxn<<1]; struct kscla { int num; kscla *Q_Next; } Q[maxn]; int P[maxn]; int fa[maxn][maxl]; int dep[maxn]; int tp[6]; int sum; int n,m; int Get(int x) { int y=0; while ( (1<<y)!=x ) y++; return y; } void Add(int x,int y) { cur++; e[cur].obj=y; e[cur].Next=head[x]; head[x]=e+cur; } void Dfs(int node) { for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if (son!=fa[node][0]) { fa[son][0]=node; dep[son]=dep[node]+1; Dfs(son); } } } bool Comp1(data x,data y) { if ( (!x.Type) && y.Type ) return true; if ( x.Type && (!y.Type) ) return false; return (x.val<y.val); } bool Comp2(data x,data y) { return (x.Time<y.Time); } void Swap(int &x,int &y) { int z=x; x=y; y=z; } int Lca(int x,int y) { if (dep[x]<dep[y]) Swap(x,y); for (kscla *p=Q+(dep[x]-dep[y]); p!=Q; p=p->Q_Next) x=fa[x][p->num]; if (x==y) return x; for (int j=P[ dep[x] ]; j>=0; j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j]; return fa[x][0]; } bool Check(int a,int x,int y,int z) { int b=Lca(a,x); int c=Lca(a,y); if (b!=z) Swap(b,c); return ( (b==z) && (c==a) ); } bool Judge(int x,int y,int a,int b,int lca) { if ( !Check(x,a,b,lca) ) return false; return Check(y,a,b,lca); } void Work(int x,int y,int z,int w,int &a,int &b) { if ( (!x) || (!z) ) { a=b=0; return; } tp[0]=Lca(x,y); tp[1]=Lca(x,z); tp[2]=Lca(x,w); tp[3]=Lca(y,z); tp[4]=Lca(y,w); tp[5]=Lca(z,w); a=b=0; int len=-1; for (int i=0; i<5; i++) for (int j=i+1; j<6; j++) { int k=Lca(tp[i],tp[j]); int l=dep[ tp[i] ]+dep[ tp[j] ]-(dep[k]<<1); if (l<=len) continue; if ( !Judge(tp[i],tp[j],x,y,tp[0]) ) continue; if ( !Judge(tp[i],tp[j],z,w,tp[5]) ) continue; a=tp[i],b=tp[j],len=l; } } void Update(int root,int L,int R,int x,int nu,int nv) { if ( x<L || R<x ) return; if ( x==L && R==x ) { tree[root].U=nu; tree[root].V=nv; if (nu) tree[root].cnt++; else tree[root].cnt--; return; } int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; Update(Left,L,mid,x,nu,nv); Update(Right,mid+1,R,x,nu,nv); if (!tree[Left].cnt) { tree[root]=tree[Right]; return; } if (!tree[Right].cnt) { tree[root]=tree[Left]; return; } tree[root].cnt=tree[Left].cnt+tree[Right].cnt; Work(tree[Left].U,tree[Left].V,tree[Right].U,tree[Right].V,tree[root].U,tree[root].V); } int Query(int root,int L,int R,int x) { if (L==R) { if (L!=1) return work[ bot[L] ].val; if (!tree[root].cnt) return -1; int W=Lca(tree[root].U,tree[root].V); if ( Check(x,tree[root].U,tree[root].V,W) ) return -1; return work[ bot[1] ].val; } int mid=(L+R)>>1; int Left=root<<1; int Right=Left|1; if (!tree[Right].cnt) return Query(Left,L,mid,x); if (!tree[Right].U) return Query(Right,mid+1,R,x); int W=Lca(tree[Right].U,tree[Right].V); if ( Check(x,tree[Right].U,tree[Right].V,W) ) return Query(Left,L,mid,x); return Query(Right,mid+1,R,x); } int main() { freopen("4538.in","r",stdin); freopen("4538.out","w",stdout); P[0]=-1; for (int i=1; i<maxn; i++) { P[i]=P[i>>1]+1; int x=i&(-i); Q[i].num=Get(x); Q[i].Q_Next=Q+(i-x); } scanf("%d%d",&n,&m); for (int i=1; i<=n; i++) head[i]=NULL; for (int i=1; i<n; i++) { int x,y; scanf("%d%d",&x,&y); Add(x,y); Add(y,x); } Dfs(1); for (int j=1; j<maxl; j++) for (int i=1; i<=n; i++) fa[i][j]=fa[ fa[i][j-1] ][j-1]; for (int i=1; i<=m; i++) { scanf("%d",&work[i].Type); if (!work[i].Type) scanf("%d%d%d",&work[i].u,&work[i].v,&work[i].val); else scanf("%d",&work[i].u); work[i].Time=i; } sort(work+1,work+m+1,Comp1); for (sum=1; sum<=m; sum++) if (!work[sum].Type) work[sum].id=sum,bot[sum]=work[sum].Time; else break; sum--; sort(work+1,work+m+1,Comp2); for (int i=1; i<=m; i++) { if (!work[i].Type) Update(1,1,sum,work[i].id,work[i].u,work[i].v); if (work[i].Type==1) Update(1,1,sum,work[ work[i].u ].id,0,0); if (work[i].Type==2) printf("%d\n", Query(1,1,sum,work[i].u) ); } return 0; }
相关文章推荐
- 【BZOJ4538】[Hnoi2016]网络 整体二分+树状数组
- Bzoj4538:[Hnoi2016]网络:整体二分+树状数组
- bzoj 4538: [Hnoi2016]网络 (整体二分+树状数组)
- 4538: [Hnoi2016]网络 链剖 + 堆(优先队列) / 整体二分
- BZOJ 4538: [Hnoi2016]网络 [整体二分]
- bzoj4538:网络(整体二分+LCA+BIT)
- [BZOJ4538][HNOI2016]网络-线段树-树链剖分
- bzoj4538 [Hnoi2016]网络
- bzoj4538: [HNOI2016]网络
- BZOJ4538 : [Hnoi2016]网络
- 【bzoj4538】【HNOI2016】【网络】【树链剖分+线段树套堆】
- bzoj4538: [Hnoi2016]网络
- bzoj4538: [Hnoi2016]网络
- 【bzoj4538】[Hnoi2016]网络
- [bzoj4538][Hnoi2016]网络
- [bzoj4538][Hnoi2016]网络
- BZOJ4538: [Hnoi2016]网络
- bzoj4538 [Hnoi2016]网络
- 【BZOJ4538】【HNOI2016】网络
- 【BZOJ4538】【HNOI2016】网络