【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述

M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络。该网络的结构由N个路由器和N-1条高速光缆组成。每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行数据交换会带来很大的延迟。而两个路由器之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来监视公司的网络状况。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通信路径上延迟第k大的路由器的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这Q条询问信息,它们可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。

输入

第一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由器初始的数据延迟时间Ti。紧接着N-1行,每行包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的数据交换延迟时间由Ta变为b。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意N,Q<=80000,任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N

输出

对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。

样例输入

5 5
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5

样例输出

3
2
2
invalid request!



题解

倍增LCA+dfs序+树状数组+主席树

维护一个dfs入栈出栈序,建立主席树,入栈位置将权值位置+1,出栈位置将权值位置-1.。

对于每个询问,转化为x->lca和y->lca的两条链,进而转化为4条路径:1~x + 1~y - 1~lca - 1~fa[lca]。

由于题目带修改,所以需要使用树状数组维护主席树的“前缀和”。然后就可以无脑码了。

需要注意的一点是本题卡空间,所以主席树的插入过程不能每次都新建节点,如果有节点就修改,没有再添加。

#include <cstdio>
#include <algorithm>
#define N 80010
using namespace std;
int w[N] , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][18] , deep[N] , log[N] , lp[N] , rp[N] , num;
int ls[N * 200] , rs[N * 200] , si[N * 200] , root[N << 1] , tot , A[40] , B[40] , C[40] , D[40] , ta , tb , tc , td;
int v[N << 1] , tv , vk[N] , vx[N] , vy[N];
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	lp[x] = ++num;
	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x][0])
			fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
	rp[x] = ++num;
}
int lca(int x , int y)
{
	int i;
	if(deep[x] < deep[y]) swap(x , y);
	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
		if(deep[x] - deep[y] >= (1 << i))
			x = fa[x][i];
	if(x == y) return x;
	for(i = log[deep[x]] ; ~i ; i -- )
		if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
			x = fa[x][i] , y = fa[y][i];
	return fa[x][0];
}
void update(int p , int a , int l , int r , int &x)
{
	if(!x) x = ++tot;
	si[x] += a;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) update(p , a , l , mid , ls[x]);
	else update(p , a , mid + 1 , r , rs[x]);
}
int query(int k , int l , int r)
{
	if(l == r) return l;
	int mid = (l + r) >> 1 , sum = 0 , i;
	for(i = 1 ; i <= ta ; i ++ ) sum += si[rs[A[i]]];
	for(i = 1 ; i <= tb ; i ++ ) sum += si[rs[B[i]]];
	for(i = 1 ; i <= tc ; i ++ ) sum -= si[rs[C[i]]];
	for(i = 1 ; i <= td ; i ++ ) sum -= si[rs[D[i]]];
	if(k <= sum)
	{
		for(i = 1 ; i <= ta ; i ++ ) A[i] = rs[A[i]];
		for(i = 1 ; i <= tb ; i ++ ) B[i] = rs[B[i]];
		for(i = 1 ; i <= tc ; i ++ ) C[i] = rs[C[i]];
		for(i = 1 ; i <= td ; i ++ ) D[i] = rs[D[i]];
		return query(k , mid + 1 , r);
	}
	else
	{
		for(i = 1 ; i <= ta ; i ++ ) A[i] = ls[A[i]];
		for(i = 1 ; i <= tb ; i ++ ) B[i] = ls[B[i]];
		for(i = 1 ; i <= tc ; i ++ ) C[i] = ls[C[i]];
		for(i = 1 ; i <= td ; i ++ ) D[i] = ls[D[i]];
		return query(k - sum , l , mid);
	}
}
int main()
{
	int n , q , i , j , x , y , f;
	scanf("%d%d" , &n , &q);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]) , v[++tv] = w[i];
	for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1;
	dfs(1);
	for(i = 1 ; i <= q ; i ++ )
	{
		scanf("%d%d%d" , &vk[i] , &vx[i] , &vy[i]);
		if(!vk[i]) v[++tv] = vy[i];
	}
	sort(v + 1 , v + tv + 1);
	for(tv = 0 , i = 1 ; i <= n + q ; i ++ )
		if(v[i] != v[i - 1])
			v[++tv] = v[i];
	for(i = 1 ; i <= n ; i ++ )
	{
		w[i] = lower_bound(v + 1 , v + tv + 1 , w[i]) - v;
		for(j = lp[i] ; j <= num ; j += j & -j) update(w[i] , 1 , 1 , tv , root[j]);
		for(j = rp[i] ; j <= num ; j += j & -j) update(w[i] , -1 , 1 , tv , root[j]);
	}
	for(i = 1 ; i <= q ; i ++ )
	{
		if(vk[i])
		{
			f = lca(vx[i] , vy[i]);
			if(deep[vx[i]] + deep[vy[i]] - 2 * deep[f] + 1 < vk[i]) puts("invalid request!");
			else
			{
				ta = tb = tc = td = 0;
				for(j = lp[vx[i]] ; j ; j -= j & -j) A[++ta] = root[j];
				for(j = lp[vy[i]] ; j ; j -= j & -j) B[++tb] = root[j];
				for(j = lp[f] ; j ; j -= j & -j) C[++tc] = root[j];
				for(j = lp[fa[f][0]] ; j ; j -= j & -j) D[++td] = root[j];
				printf("%d\n" , v[query(vk[i] , 1 , tv)]);
			}
		}
		else
		{
			vy[i] = lower_bound(v + 1 , v + tv + 1 , vy[i]) - v;
			for(j = lp[vx[i]] ; j <= num ; j += j & -j) update(w[vx[i]] , -1 , 1 , tv , root[j]) , update(vy[i] , 1 , 1 , tv , root[j]);
			for(j = rp[vx[i]] ; j <= num ; j += j & -j) update(w[vx[i]] , 1 , 1 , tv , root[j]) , update(vy[i] , -1 , 1 , tv , root[j]);
			w[vx[i]] = vy[i];
		}
	}
	return 0;
}
时间: 2024-08-02 02:50:53

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树的相关文章

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

bzoj[3881]Divljak(dfs序+树状数组+fail树)

这道题利用了fail树的神奇性质————父节点为其子节点的前缀 先对Alice的集合建一个fail树, Bob每插入一个串,都将串在自动机上经过的点在树上打上标记(+1) 每次查询的答案就是询问串的结束节点的子树的贡献 所以还需要用到树状数组来维护dfs序 因为Bob的一个串至多只能对Alice的某一个串做出1的贡献,所以不能单纯加和 要对树链取并 可以将一个Bob串在fail树上经过的所有点按入栈顺序排序,相邻每两个点的lca贡献-1 可以证明,这样会使每次某个点的子树内没有重复贡献 1 #i

CCPC河南省赛B-树上逆序对| 离线处理|树状数组 + 线段树维护逆序对 + dfs序 + 离散化

B题地址:树上逆序对 有两个思路 方法一:线段树离线 + 树状数组或者线段树维护区间和 0:离散化,离线存储输入的operation操作序列. ①:先线段树在dfs序上离线处理好整一棵树:在dfs序上先查询"加入当前结点的逆序对权值和"并记录,再加入当前这个节点:dfs完毕后,就已经记录好每个结点的dfs序出入时间戳(转化成区间问题了)和每个 ②:使用树状数组或者新的线段树在dfs序上插入逆序对权值 为什么能这样呢?因为dfs序维护了每个结点遍历的顺序,每个结点的dfs序时间戳肯定比它

bzoj1146 [CTSC2008]网络管理Network

Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由

BZOJ1146 [CTSC2008]网络管理Network 树链剖分 主席树 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链上的第k大值 题解 水题. 就是烦了一点. 树链剖分+带修主席树. 带修主席树: BZOJ1901 Zju2112 Dynamic Rankings 主席树 代码 #include <cstring> #include <cstdio> #include <algorithm&g

【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network

裸题,直接上.复杂度O(n*sqrt(n)*log(n)). //Num[i]表示树中的点i在函数式权值分块中对应的点 //Map[i]表示函数式权值分块中的点i在树中对应的点 #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 80001 #define INF 2147483647 #define NN 87001 #define BN 296 int

【bzoj2819】Nim DFS序+树状数组+倍增LCA

题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家.为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达.然后他不停地进行如下操

BZOJ 1146: [CTSC2008]网络管理Network [树上带修改主席树]

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 3522  Solved: 1041[Submit][Status][Discuss] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成. 每个部门都有一个专属的路由器,

BZOJ 1146: [CTSC2008]网络管理Network

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 3539  Solved: 1054[Submit][Status][Discuss] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个 部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成. 每个部门都有一个专属的路由器,