[HNOI2016]网络

Posted by Dispwnl on December 5, 2018

没有什么是四百万个堆不能搞定的,如果有,那就八百万的堆

题目

题目描述

一个简单的网络系统可以被描述成一棵无根树。每个节点为一个服务器。连接服务器与服务器的数据线则看做一条树边。两个服务器进行数据的交互时,数据会经过连接这两个服务器的路径上的所有服务器(包括这两个服务器自身)。

由于这条路径是唯一的,当路径上的某个服务器出现故障,无法正常运行时,数据便无法交互。此外,每个数据交互请求都有一个重要度,越重要的请求显然需要得到越高的优先处理权。现在,你作为一个网络系统的管理员,要监控整个系统的运行状态。系统的运行也是很简单的,在每一个时刻,只有可能出现下列三种事件中的一种:

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

某个数据交互结束请求;

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

你的任务是在每次出现故障时,维护未被影响的请求中重要度的最大值。注意,如果一个数据交互请求已经结束,则不将其纳入未被影响的请求范围。

输入输出格式

输入格式:

第一行两个正整数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

输出格式:

对于每个type=2的事件,即服务器出现故障的事件,输出一行一个整数,描述未被影响的请求中重要度的最大值。如果此时没有任何请求,或者所有请求均被影响,则输出-1。

输入输出样例

输入样例#1:

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

输出样例#1:

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

说明

样例给出的树如下所示:

解释其中的部分询问;下面的解释中用(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。

题解

对于每个节点,可以维护当前加入的交互请求不经过这个点的值

考虑树链剖分,每个线段树节点维护一个大根堆$A$(……),存不经过这个区间的请求值

这样每次加入请求就是修改树上这条链外其他部分

树链剖分修改一条链需要跳重链,即把一条链分成几段$dfn$序连续的区间,所以可以记录这些区间并排序,修改之外的区间

根据树剖的性质,区间数不超过$\logn$个,所以复杂度$O(\log^3n)$(……),但是考虑到大区间维护的堆肯定会影响子区间维护的值,所以线段树不用下传和上传,复杂度应该远远不到$O(\log^3n)$

操作$3​$就是单点查询,操作$2​$怎么维护呢?

考虑再维护一个堆$B$存这个节点应该去掉的值(……),如果$dis_{top_A}=dis_{top_B}$,说明这个值已经被去除了,两边都弹出,直到$dis_{top_A}\not =dis_{top_B}$或者$-1$的情况

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<queue>
# include<algorithm>
# define tl (k<<1)
# define tr (k<<1|1)
# define mid (l+r>>1) 
using namespace std;
const int MAX=1e5+5;
struct p{
	priority_queue<int> qu,_qu;
}s[MAX<<2];
struct q{
	int x,y;
	bool operator< (const q &a)
	const{
		return x<a.x;
	}
}c[MAX<<1];
struct o{
	int l,r,v;
}qu[MAX<<1];
int n,m,num,cnt;
int h[MAX],f[MAX],siz[MAX],son[MAX],d[MAX],top[MAX],id[MAX];
vector<q> L[MAX<<1];
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void add()
{
	int x=read(),y=read();
	c[++num]=(q){h[x],y},h[x]=num;
	c[++num]=(q){h[y],x},h[y]=num;
}
void dfs(int x=1,int fa=0)
{
	f[x]=fa,d[x]=d[fa]+(siz[x]=1);
	for(int i=h[x];i;i=c[i].x)
	  if(c[i].y^fa)
	  {
	  	dfs(c[i].y,x),siz[x]+=siz[c[i].y];
	  	if(siz[son[x]]<siz[c[i].y]) son[x]=c[i].y;
	  }
}
void dfs1(int x=1,int tp=1)
{
	top[x]=tp,id[x]=++cnt;
	if(!son[x]) return;
	dfs1(son[x],tp);
	for(int i=h[x];i;i=c[i].x)
	  if((c[i].y^f[x])&&(c[i].y^son[x])) dfs1(c[i].y,c[i].y);
}
void change(int l,int r,int k,int L,int R,int dis)
{
	if(L>R) return;
	if(l==L&&r==R) return void(s[k].qu.push(dis));
	if(R<=mid) change(l,mid,tl,L,R,dis);
	else if(L>mid) change(mid+1,r,tr,L,R,dis);
	else change(l,mid,tl,L,mid,dis),change(mid+1,r,tr,mid+1,R,dis);
}
void CHANGE(int k,int x,int y,int dis)
{
	int cnt=0;
	while(top[x]^top[y])
	{
		if(d[top[x]]<d[top[y]]) swap(x,y);
		L[k].push_back((q){id[top[x]],id[x]}),++cnt,x=f[top[x]];
	}
	if(d[x]>d[y]) swap(x,y);
	L[k].push_back((q){id[x],id[y]}),++cnt,sort(L[k].begin(),L[k].end());
	change(1,n,1,1,L[k][0].x-1,dis);
	for(int i=1;i<cnt;++i)
	  change(1,n,1,L[k][i-1].y+1,L[k][i].x-1,dis);
	change(1,n,1,L[k][cnt-1].y+1,n,dis);
}
void change1(int l,int r,int k,int L,int R,int dis)
{
	if(L>R) return;
	if(l==L&&r==R) return void(s[k]._qu.push(dis));
	if(R<=mid) change1(l,mid,tl,L,R,dis);
	else if(L>mid) change1(mid+1,r,tr,L,R,dis);
	else change1(l,mid,tl,L,mid,dis),change1(mid+1,r,tr,mid+1,R,dis);
}
void CHANGE1(int k)
{
	int cnt=L[k].size();
	change1(1,n,1,1,L[k][0].x-1,qu[k].v);
	for(int i=1;i<cnt;++i)
	  change1(1,n,1,L[k][i-1].y+1,L[k][i].x-1,qu[k].v);
	change1(1,n,1,L[k][cnt-1].y+1,n,qu[k].v);
}
int GET_ANS(p a)
{
	if(a.qu.empty()) return -1;
	if(a._qu.empty()) return a.qu.top();
	int A=a.qu.top(),B=a._qu.top();
	while(A==B)
	{
		a.qu.pop(),a._qu.pop();
		if(a.qu.empty()||a._qu.empty()) break;
		A=a.qu.top(),B=a._qu.top();
	}
	if(a.qu.empty()) return -1;
	return a.qu.top();
}
int ask(int l,int r,int k,int x)
{
	int qwq=GET_ANS(s[k]);
	if(l==r) return qwq;
	if(x<=mid) return max(qwq,ask(l,mid,tl,x));
	return max(qwq,ask(mid+1,r,tr,x));
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<n;++i,add());
	dfs(),dfs1();
	for(int i=1,op,x,y,v;i<=m;++i)
	  {
	  	op=read(),x=read();
	  	if(!op) y=read(),v=read(),qu[i]=(o){x,y,v},CHANGE(i,x,y,v);
	  	else if(op==1) CHANGE1(x);
	  	else if(op==2) printf("%d\n",ask(1,n,1,id[x]));
	  }
	return 0;
}