zoukankan      html  css  js  c++  java
  • 【BZOJ4538】[HNOI2016] 网络(整体二分+树状数组)

    点此看题面

    大致题意: 给定一棵无根树,三种操作:建立一个权值(x)的经过两点树上路径的任务;取消之前的一个任务;询问所有不经过树上给定点的任务中最大的权值。

    整体二分

    看到这道题,我们首先应该想到整体二分。(实际上我这种奇葩首先想到的是树套树

    考虑以答案为区间二分,每次把权值小于等于(mid)的任务扔到左区间,权值大于(mid)的任务在树上修改并扔到右区间。

    接下来对于一个询问,我们只要判断此时若存在不经过该点的任务,这个询问的答案就大于(mid),否则就小于等于(mid)

    然后就是如何有效维护信息以及修改的问题了。

    树上差分

    二分之后的每次修改相当于给一条树上路径加/减(1)

    树上路径?我会树剖!

    然而,其实这种题目只要树上差分一下就可以了。

    考虑对((x,y))间的路径加上(1),只要给(x,y)分别加(1),然后给(LCA(x,y))(1)(fa_{LCA(x,y)})(1),则一个点的点权就相当于是它子树内的点权和。

    众所周知,子树在(dfs)序列上是一段区间。

    于是就变成了单点修改、区间查询,显然一个树状数组就好了。

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define M 200000
    #define LN 20
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,m,dc,dv[M+5],d,dI[N+5],dO[N+5],dep[N+5],fa[N+5][LN+5];
    int ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    struct Op {int op,x,y,v;}q[M+5],sl[M+5],sr[M+5];int ans[M+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void writeNA() {pc('-'),pc('1'),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    I void dfs(CI x)//dfs初始化
    {
    	RI i;for(dI[x]=++d,i=1;i<=LN;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
    	for(i=lnk[x];i;i=e[i].nxt) fa[x][0]^e[i].to&&
    		(dep[e[i].to]=dep[fa[e[i].to][0]=x]+1,dfs(e[i].to),0);dO[x]=d;
    }
    I int LCA(RI x,RI y)//倍增LCA
    {
    	RI i;dep[x]<dep[y]&&swap(x,y);
    	for(i=0;dep[x]^dep[y];++i) (dep[x]^dep[y])>>i&1&&(x=fa[x][i]);if(x==y) return x;
    	for(i=LN;~i;--i) fa[x][i]^fa[y][i]&&(x=fa[x][i],y=fa[y][i]);return fa[x][0];
    }
    class TreeArray//树状数组
    {
    	private:
    		int a[M+5];
    	public:
    		I void U(RI x,CI y) {W(x<=n) a[x]+=y,x+=x&-x;}//单点修改
    		I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}//询问前缀和(差分求出区间和)
    }T;int cnt;
    I void F5(CI id,CI op)//根据一个询问差分修改
    {
    	RI z=LCA(q[id].x,q[id].y),v=q[id].v<0?-op:op;cnt+=v;
    	T.U(dI[q[id].x],v),T.U(dI[q[id].y],v),T.U(dI[z],-v),z^1&&(T.U(dI[fa[z][0]],-v),0);
    }
    I void Solve(CI l,CI r,CI L,CI R)//整体二分
    {
    	RI i;if(l==r) {for(i=L;i<=R;++i) q[i].op&&(ans[q[i].v]=l);return;}//边界
    	RI mid=l+r>>1,tl=0,tr=0;for(i=L;i<=R;++i)
    	{
    		if(!q[i].op) {(abs(q[i].v)<=mid?sl[++tl]:(F5(i,1),sr[++tr]))=q[i];continue;}//对于任务
    		((T.Q(dO[q[i].x])-T.Q(dI[q[i].x]-1))^cnt?sr[++tr]:sl[++tl])=q[i];//对于询问
    	}
    	for(i=L;i<=R;++i) !q[i].op&&abs(q[i].v)>mid&&(F5(i,-1),0);//清空
    	for(i=1;i<=tl;++i) q[L+i-1]=sl[i];for(i=1;i<=tr;++i) q[L+tl-1+i]=sr[i];//把分到两边的询问放回原数组
    	Solve(l,mid,L,L+tl-1),Solve(mid+1,r,L+tl,R);//递归
    }
    int main()
    {
    	RI i,x,y;for(F.read(n,m),i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);dfs(1);
    	for(i=1;i<=m;++i) F.read(q[i].op,q[i].x),!q[i].op&&(F.read(q[i].y,q[i].v),dv[++dc]=q[i].v);
    	RI t=0;for(sort(dv+1,dv+dc+1),dc=unique(dv+1,dv+dc+1)-dv-1,i=1;i<=m;++i) switch(q[i].op)
    	{
    		case 0:q[i].v=lower_bound(dv+1,dv+dc+1,q[i].v)-dv;break;//离散化
    		case 1:q[i]=q[q[i].x],q[i].v*=-1;break;case 2:q[i].v=++t;break;//v<0表示删除
    	}Solve(0,dc,1,m);
    	for(i=1;i<=t;++i) ans[i]?F.writeln(dv[ans[i]]):F.writeNA();return F.clear(),0;//输出答案
    }
    
  • 相关阅读:
    多个类定义attr属性重复的问题:Attribute "xxx" has already been defined
    好用的批量改名工具——文件批量改名工具V2.0 绿色版
    得到ImageView中drawable显示的区域的计算方法
    得到view坐标的各种方法
    实现类似于QQ空间相册的点击图片放大,再点后缩小回原来位置
    Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
    Android 自带图标库 android.R.drawable
    解决 Attempting to destroy the window while drawing!
    解决Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 4
    Material Designer的低版本兼容实现(四)—— ToolBar
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4538.html
Copyright © 2011-2022 走看看