[bzoj4538][Hnoi2016]网络
来自FallDream的博客,未经允许,请勿转载,谢谢。
一个简单的网络系统可以被描述成一棵无根树。每个节点为一个服务器。连接服务器与服务器的数据线则看做一条树边。两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身)。由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互。此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权。现在,你作为一个网络系统的管理员,要监控整个系统的运行状态。系统的运行也是很简单的,在每一个时刻,只有可能出现下列三种事件中的一种:1. 在某两个服务器之间出现一条新的数据交互请求;2. 某个数据交互结束请求;3. 某个服务器出现故障。系统会在任何故障发生后立即修复。也就是在出现故障的时刻之后,这个服务器依然是正常的。但在服务器产生故障时依然会对需要经过该服务器的数据交互请求造成影响。你的任务是在每次出现故障时,维护未被影响的请求中重要度的最大值。注意,如果一个数据交互请求已经结束,则不将其纳入未被影响的请求范围。
就是要支持加入/删除一条有权值的链,询问不过一个点的链的权值最大值
n<=10^5 m<=2*10^5
如果树剖的话,可以把区间取反,就等于是包含一个点的线段的权值最大值了 但是那样的话脑补一下可以发现是3个log的 还容易炸空间(我并不知道那些人是怎么过的)
然后貌似有种做法比较靠谱,就是线段树维护权值区间的所有链的交,询问时候在线段树上二分,配合O(1)的lca查询的话可以做到O(nlogn)复杂度,倍增求lca的话两个log,就已经可以过啦。
然后我的做法是直接整体二分,然后每次判断每个询问的答案大于/小于二分的值。
把大于二分值的操作都拿出来,变成整条链加/减1,询问一个点的权值。如果一个点的权值等于现在存在的链的数量,那么显然答案小等于二分值,否则答案是大于的。
链加/单点查,我们求出lca配合dfs序就变成了单点改,区间查,用线段树可以轻松实现。
复杂度nlog^2n
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #define MN 200000 #define MD 17 #define N 131072 using namespace std; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } int n,m,head[MN+5],nl[MN+5],nr[MN+5],dn,cnt,T[N*2+5],q[MN+5],q1[MN+5],q2[MN+5]; int top1,top2,fa[MD+1][MN+5],num,l[MN+5],tot=1,Ans[MN+5],dep[MN+5]; struct edge{int to,next,w;}e[MN*2+5]; struct data{int kind,x,y,z,w,ad;}s[MN+5]; inline void ins(int f,int t) { e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } void Pre(int x,int f) { nl[x]=++dn;fa[0][x]=f; for(int i=head[x];i;i=e[i].next) if(e[i].to!=f) dep[e[i].to]=dep[x]+1,Pre(e[i].to,x); nr[x]=dn; } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int k=dep[x]-dep[y],j=0;k;k>>=1,++j) if(k&1) x=fa[j][x]; if(x==y) return x; for(int i=MD;~i;--i) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y]; return fa[0][x]; } void Modify(int x,int ad) { T[x+=N]+=ad; for(x>>=1;x;x>>=1) T[x]=T[x<<1]+T[x<<1|1]; } int Query(int l,int r) { int sum=0; for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1) sum+=T[l+1]; if( r&1) sum+=T[r-1]; } return sum; } void Solve(int l,int r,int lt,int rt) { if(l>r) return; if(lt==rt) { for(int i=l;i<=r;++i) if(s[q[i]].kind) Ans[q[i]]=lt; return; } int mid=lt+rt>>1,sum=0;top1=top2=0; for(int i=l;i<=r;++i) if(s[q[i]].kind) { if(Query(nl[s[q[i]].x],nr[s[q[i]].x])==sum) q1[++top1]=q[i]; else q2[++top2]=q[i]; } else { if(s[q[i]].w>mid) { sum+=s[q[i]].ad; Modify(nl[s[q[i]].x],s[q[i]].ad); Modify(nl[s[q[i]].y],s[q[i]].ad); Modify(nl[s[q[i]].z],-s[q[i]].ad); if(s[q[i]].z!=1) Modify(nl[fa[0][s[q[i]].z]],-s[q[i]].ad); q2[++top2]=q[i]; } else q1[++top1]=q[i]; } for(int i=l;i<=r;++i) if(s[q[i]].kind==0&&s[q[i]].w>mid) { Modify(nl[s[q[i]].x],-s[q[i]].ad); Modify(nl[s[q[i]].y],-s[q[i]].ad); Modify(nl[s[q[i]].z],s[q[i]].ad); if(s[q[i]].z!=1) Modify(nl[fa[0][s[q[i]].z]],s[q[i]].ad); } for(int i=1;i<=top1;++i) q[l+i-1]=q1[i]; for(int i=1;i<=top2;++i) q[l+top1+i-1]=q2[i]; int pre=top1;Solve(l,l+top1-1,lt,mid);Solve(l+pre,r,mid+1,rt); } int main() { n=read();m=read(); for(int i=1;i<n;++i) ins(read(),read()); Pre(1,0);l[++num]=-1; for(int i=1;i<=MD;++i) for(int j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]]; for(int i=1;i<=m;++i) { int op=read(); q[i]=i; if(op==0) s[i].x=read(),s[i].y=read(),s[i].z=lca(s[i].x,s[i].y),s[i].w=l[++num]=read(),s[i].ad=1; if(op==1) s[i]=s[read()],s[i].ad=-1; if(op==2) s[i].kind=1,s[i].x=read(); } sort(l+1,l+num+1); for(int i=2;i<=num;++i) if(l[i]!=l[i-1]) l[++tot]=l[i]; for(int i=1;i<=m;++i) if(!s[i].kind) s[i].w=lower_bound(l+1,l+tot+1,s[i].w)-l; Solve(1,m,1,tot); for(int i=1;i<=m;++i) if(s[i].kind) printf("%d\n",l[Ans[i]]); return 0; }