题解 LOJ2049 「HNOI2016」网络

题目链接

简要题意:给定一棵树。让你支持三种操作:

  1. 加入一条路径((u,v))。路径有一个权值(w)
  2. 删除之前某一时刻加入的路径。
  3. 对树上某个节点(u)询问:问当前所有不经过(u)的路径的权值的最大值。

可以发现,本题的难点在于最大值,它不像类似于“求权值和”这样的问题,它不支持撤销,也就是没有“可减性”。因此我们很难通过一般的数据结构,完成删除操作,并维护最大值。

那么换个思路:既然不能直接维护答案,考虑对于每次询问都二分答案。二分之后,求最大值的问题,就转化为了数路径数量的问题。具体来说,设二分值为( ext{mid})。那么考虑所有权值大于( ext{mid})的路径。如果这些路径全部覆盖了询问点(u),则该询问的答案小于等于( ext{mid}),否则该询问的答案大于( ext{mid})

加入、删除一条路径。以加入为例。可以令路径的两个端点点权(+1),LCA和LCA的父亲点权(-1),则经过某个节点(u)的路径数量,就是(u)子树内所有节点的点权和。求出dfs序后,问题转化为单点修改、区间求和。可以用树状数组实现,常数很小。

如果对每次询问都二分答案,然后暴力加入这些路径,则单次询问的时间复杂度达到(O(nlog^2n)),总时间复杂度(O(mnlog^2n)),无法承受。

注意到,对于任何一个询问,假设二分值为( ext{mid}),要考虑的都是权值大于( ext{mid})的这些路径。也就是说,只要二分值相同,对于不同的询问,我们在树状数组上加入的是同一些路径!于是我们考虑把这样的加入路径的操作放在一起做。简单讲,我们之前的做法是:对每个询问,求出二分值,做一遍加入路径的操作;现在变成:对每个二分值,加入对应的路径,然后一次性处理所有对应的询问。这就是整体二分。

我们把它写成一个分治的过程。每次分治传入的参数是两个东西:(1) 当前二分的答案区间([l,r]),(2) 这个区间对应的操作。这些操作又分为两种,一是路径权值在([l,r])之间的插入、删除操作,二是我们已经确定其答案在([l,r])之间的询问操作。每次传入的这些操作在传入时是按时间顺序排好的。我们只要按顺序处理它们,然后将其分配到左半边、右半边去即可。

时间复杂度(O(mlog^2n))

参考代码(在LOJ查看):

//problem:LOJ2049
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=1e5,MAXM=2e5;
int n,m;
struct EDGE{int nxt,to;}edge[MAXN*2+5];
int head[MAXN+5],tot;
inline void add_edge(int u,int v){edge[++tot].nxt=head[u],edge[tot].to=v,head[u]=tot;}
int fa[MAXN+5],sz[MAXN+5],son[MAXN+5],dep[MAXN+5];
void dfs1(int u){
	sz[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa[u])continue;
		fa[v]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		sz[u]+=sz[v];
		if(!son[u]||sz[v]>sz[son[u]])son[u]=v;
	}
}
int top[MAXN+5],dfn[MAXN+5],ofn[MAXN+5],rev[MAXN+5],cnt_dfn;
void dfs2(int u,int t){
	top[u]=t;
	dfn[u]=++cnt_dfn;
	rev[cnt_dfn]=u;
	if(son[u])dfs2(son[u],t);
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
	ofn[u]=cnt_dfn;
}
int get_lca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	return (dep[u]<dep[v])?u:v;
}

struct FenwickTree{
	int c[MAXN+5];
	void modify(int p,int x){
		for(;p<=n;p+=(p&(-p)))c[p]+=x;
	}
	int query(int p){
		int res=0;
		for(;p;p-=(p&(-p)))res+=c[p];
		return res;
	}
	int query(int l,int r){
		return query(r)-query(l-1);
	}
	FenwickTree(){}
}T;

void path_modify(int u,int v,int x){
	T.modify(dfn[u],x);
	T.modify(dfn[v],x);
	int lca=get_lca(u,v);
	T.modify(dfn[lca],-x);
	if(fa[lca])T.modify(dfn[fa[lca]],-x);
}

int ans[MAXM+5];
struct Query_t{
	int op,u,v,w,id;
}q[MAXM+5];
void solve(int ans_l,int ans_r,int pos_l,int pos_r){
	if(ans_l==ans_r){
		for(int i=pos_l;i<=pos_r;++i)if(q[i].op==2)ans[q[i].id]=ans_l;
		return;
	}
	static Query_t que_l[MAXM+5],que_r[MAXM+5];
	int mid=(ans_l+ans_r)>>1;
	int cnt_l=0,cnt_r=0,sum=0;
	bool have_query_l=false,have_query_r=false;
	for(int i=pos_l;i<=pos_r;++i){
		if(q[i].op==2){
			if(T.query(dfn[q[i].u],ofn[q[i].u])==sum){
				que_l[++cnt_l]=q[i];
				have_query_l=true;
			}
			else{
				que_r[++cnt_r]=q[i];
				have_query_r=true;
			}
		}
		else{
			if(q[i].w<=mid){
				que_l[++cnt_l]=q[i];
			}
			else{
				int x=(q[i].op==0?1:-1);
				path_modify(q[i].u,q[i].v,x);
				sum+=x;
				que_r[++cnt_r]=q[i];
			}
		}
	}
	for(int i=pos_l;i<=pos_r;++i){
		if(q[i].op!=2&&q[i].w>mid){
			int x=(q[i].op==0?1:-1);
			path_modify(q[i].u,q[i].v,-x);
		}
	}//树状数组清空
	for(int i=1;i<=cnt_l;++i)q[pos_l+i-1]=que_l[i];
	for(int i=1;i<=cnt_r;++i)q[pos_l+cnt_l+i-1]=que_r[i];
	if(have_query_l)solve(ans_l,mid,pos_l,pos_l+cnt_l-1);
	if(have_query_r)solve(mid+1,ans_r,pos_l+cnt_l,pos_r);
}

int main() {
	cin>>n>>m;
	for(int i=1,u,v;i<n;++i)cin>>u>>v,add_edge(u,v),add_edge(v,u);
	dfs1(1);dfs2(1,1);
	int max_w=0;
	for(int i=1;i<=m;++i){
		cin>>q[i].op;
		q[i].id=i;
		if(q[i].op==0){
			cin>>q[i].u>>q[i].v>>q[i].w;
			max_w=max(max_w,q[i].w);
			ans[i]=-3;
		}
		else if(q[i].op==1){
			int t;cin>>t;
			q[i].u=q[t].u;
			q[i].v=q[t].v;
			q[i].w=q[t].w;
			ans[i]=-3;
		}
		else{
			cin>>q[i].u;
		}
	}
	solve(-1,max_w,1,m);
	for(int i=1;i<=m;++i)if(ans[i]!=-3)cout<<ans[i]<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/12893622.html