BZOJ4538: [Hnoi2016]网络

Posted 符拉迪沃斯托克

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4538: [Hnoi2016]网络相关的知识,希望对你有一定的参考价值。

BZOJ4538: [Hnoi2016]网络

Description

一个简单的网络系统可以被描述成一棵无根树。每个节点为一个服务器。

连接服务器与服务器的数据线则看做一条树边。

两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身)。

由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互。

此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权。

现在,你作为一个网络系统的管理员,要监控整个系统的运行状态。

系统的运行也是很简单的,在每一个时刻,只有可能出现下列三种事件中的一种:

1.  在某两个服务器之间出现一条新的数据交互请求;

2.  某个数据交互结束请求;

3.  某个服务器出现故障。

系统会在任何故障发生后立即修复。也就是在出现故障的时刻之后,这个服务器依然是正常的。但在服务器产生故障时依然会对需要经过该服务器的数据交互请求造成影响。

你的任务是在每次出现故障时,维护未被影响的请求中重要度的最大值。

注意,如果一个数据交互请求已经结束,则不将其纳入未被影响的请求范围。

Input

第一行两个正整数n,m,分别描述服务器和事件个数。服务器编号是从1开始的,因此n个服务器的编号依次是1,2,3,...,n。

接下来n-1行,每行两个正整数u,v,描述一条树边。u和v是服务器的编号。

接下来m行,按发生时刻依次描述每一个事件;即第i行(i=1,2,3,...,m)描述时刻i发生的事件。每行的第一个数type描述事件类型,共3种类型:

(1)若type=0,之后有三个正整数a,b,v,表示服务器a,b之间出现一条重要度为v的数据交互请求;

(2)若type=1,之后有一个正整数t,表示时刻t(也就是第t个发生的事件)出现的数据交互请求结束;

(3)若type=2,之后有一个正整数x,表示服务器x在这一时刻出现了故障。

对于每个type为2的事件,就是一次询问,即询问”当服务器x发生故障时,未被影响的请求中重要度的最大值是多少?“注意可能有某个服务器自身与自身进行数据交互的情况。2 <= n <= 10^5, 1 <= m <= 2×10^5,其他的所有输入值不超过 10^9

Output

对于每个type=2的事件,即服务器出现故障的事件,输出一行一个整数,描述未被影响的请求中重要度的最大值。

如果此时没有任何请求,或者所有请求均被影响,则输出-1。

Sample Input

13 23
1 2
1 3
2 4
2 5
3 6
3 7
4 8
4 9
6 10
6 11
7 12
7 13
2 1
0 8 13 3
0 9 12 5
2 9
2 8
2 2
0 10 12 1
2 2
1 3
2 7
2 1
0 9 5 6
2 4
2 5
1 7
0 9 12 4
0 10 5 7
2 1
2 4
2 12
1 2
2 5
2 3

Sample Output

-1
3
5
-1
1
-1
1
1
3
6
7
7
4
6

HINT

 

样例给出的树如下所示:

技术分享图片

解释其中的部分询问;下面的解释中用(a,b;t,v)表示在t时刻出现的服务器a和b之间的重

要度为v的请求:

对于第一个询问(在时刻1),此时没有任何请求,输出-1。

对于第四个询问(在时刻6),此时有两条交互(8,13;2,3),(9,12;3,5),所有询问均经过2

号服务器,输出-1。

对于第五个询问(在时刻8),此时有三条交互(8,13;2,3),(9,12;3,5),(10,12;7,1),只有交互

(10,12;7,1)没有经过2号服务器,因此输出其重要度1。

对于最后一个询问(在时刻23),此时有三条交互(9,5;12,6),(9,12;16,4),(10,5;17,7)。当3

号服务器出现故障时,只有交互(9,5;12,6)没有经过3号服务器,因此输出6。

题解Here!

暂时没有想出 LCT 怎么做,只能拿 树链剖分 搞搞事。。。

先把树剖了,再用线段树维护。

不是普通线段树,而是 标记不下传的线段树,好像又叫 标记永久化线段树?

线段树维护什么呢?如果是区间最大值,不能支持删除,于是想起了堆。

插入就是线段树中找到区间,将值插入对应区间的堆中。

删除同理,将值插入另外一个堆中。

求某个点的最大值,怎么办?

将线段树中一条路径所有区间的最大值求最大值,即为答案。

每个区间先要删除两个堆中堆顶相同的元素,再去插入值的堆顶,即为最大值。

这就是为什么线段树的标记不能下传。

还有个原因:下传了就会MLE。。。

但是求的是未被影响的区间,怎么办?

我们先将树上两点对之间的树链求出,排个序,就转换成了线段树中的一个个区间,再将区间反转,即处理不在这些区间范围内的所有区间,即可。

注:正常的树链剖分可能MLE,应该是数据的锅。。。

可以将 dfs1 中的 > 改为 >= 就可以过了。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define LSON rt<<1
#define RSON rt<<1|1
#define INSERT(x,k) b[x].insert(k)
#define REMOVE(x,k) b[x].remove(k)
#define TOP(x) b[x].top()
#define LSIDE(x) b[x].l
#define RSIDE(x) b[x].r
#define MAXN 100010
using namespace std;
int n,m,c=1,d=1;
int head[MAXN],deep[MAXN],fa[MAXN],son[MAXN],size[MAXN],id[MAXN],top[MAXN];
struct node1{
	int next,to;
}a[MAXN<<1];
struct node2{
	priority_queue<int> q_one,q_two;
	void insert(int x){q_one.push(x);}
	void remove(int x){q_two.push(x);}
	int top(){
		while(!q_two.empty()&&q_one.top()==q_two.top()){q_one.pop();q_two.pop();}
		return (q_one.empty()?-1:q_one.top());
	}
	int l,r;
}b[MAXN<<2];
struct node3{
	int u,v,w;
}g[MAXN<<1];
struct node4{
	int l,r;
}h[MAXN<<1];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<‘0‘||c>‘9‘){if(c==‘-‘)w=-1;c=getchar();}
	while(c>=‘0‘&&c<=‘9‘){date=date*10+c-‘0‘;c=getchar();}
	return date*w;
}
bool cmp(const node4 &x,const node4 &y){
	if(x.l==y.l)return x.r<y.r;
	return x.l<y.l;
}
void buildtree(int l,int r,int rt){
	int mid;
	LSIDE(rt)=l;
	RSIDE(rt)=r;
	if(l==r)return;
	mid=l+r>>1;
	buildtree(l,mid,LSON);
	buildtree(mid+1,r,RSON);
}
void update_one(int l,int r,int c,int rt){
	int mid;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
		INSERT(rt,c);
		return;
	}
	mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)update_one(l,r,c,LSON);
	if(mid<r)update_one(l,r,c,RSON);
}
void update_two(int l,int r,int c,int rt){
	int mid;
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
		REMOVE(rt,c);
		return;
	}
	mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)update_two(l,r,c,LSON);
	if(mid<r)update_two(l,r,c,RSON);
}
int query(int l,int r,int rt){
	int mid,ans=TOP(rt);
	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return ans;
	mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(l<=mid)ans=max(ans,query(l,r,LSON));
	if(mid<r)ans=max(ans,query(l,r,RSON));
	return ans;
}
inline void add(int x,int y){
	a[c].to=y;
	a[c].next=head[x];
	head[x]=c++;
	a[c].to=x;
	a[c].next=head[y];
	head[y]=c++;
}
void dfs1(int rt){
	son[rt]=0;size[rt]=1;
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(!deep[will]){
			deep[will]=deep[rt]+1;
			fa[will]=rt;
			dfs1(will);
			size[rt]+=size[will];
			if(size[son[rt]]<=size[will])son[rt]=will;
		}
	}
}
void dfs2(int rt,int f){
	id[rt]=d++;top[rt]=f;
	if(son[rt])dfs2(son[rt],f);
	for(int i=head[rt];i;i=a[i].next){
		int will=a[i].to;
		if(will!=fa[rt]&&will!=son[rt])
		dfs2(will,will);
	}
}
void work1(int x,int y,int k){
	int f=0,left=0;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		f++;h[f].l=id[top[x]];h[f].r=id[x];
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	f++;h[f].l=id[x];h[f].r=id[y];
	sort(h+1,h+f+1,cmp);
	for(int i=1;i<=f;left=h[i++].r)if(left+1<h[i].l)update_one(left+1,h[i].l-1,k,1);
	if(left<n)update_one(left+1,n,k,1);
}
void work2(int x,int y,int k){
	int f=0,left=0;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		f++;h[f].l=id[top[x]];h[f].r=id[x];
		x=fa[top[x]];
	}
	if(deep[x]>deep[y])swap(x,y);
	f++;h[f].l=id[x];h[f].r=id[y];
	sort(h+1,h+f+1,cmp);
	for(int i=1;i<=f;left=h[i++].r)if(left+1<h[i].l)update_two(left+1,h[i].l-1,k,1);
	if(left<n)update_two(left+1,n,k,1);
}
void work(){
	int f,x;
	for(int i=1;i<=m;i++){
		f=read();x=read();
		if(f==0){
			g[i].u=x;g[i].v=read();g[i].w=read();
			work1(g[i].u,g[i].v,g[i].w);
		}
		if(f==1)work2(g[x].u,g[x].v,g[x].w);
		if(f==2)printf("%d\n",query(id[x],id[x],1));
	}
}
void init(){
	int x,y;
	n=read();m=read();
	for(int i=1;i<n;i++){
		x=read();y=read();
		add(x,y);
	}
	deep[1]=1;
	dfs1(1);
	dfs2(1,1);
	buildtree(1,n,1);
}
int main(){
	init();
	work();
	return 0;
}

 





































































以上是关于BZOJ4538: [Hnoi2016]网络的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4538 [Hnoi2016]网络

bzoj4538: [Hnoi2016]网络

bzoj 4538: [Hnoi2016]网络

网络(bzoj 4538)

bzoj4011 hnoi2015落忆枫音

[BZOJ4542][HNOI2016]大数(莫队)