[CTSC2008]网络管理 整体二分+树状数组+树链剖分

题意:

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大的路由器的延迟时间。注意a可以等于b,此时路径上只有一个路由器。

输出:

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

样例略。

思路:

首先这道题还是多次查询链上的第k大值,注意不是第k小,所以还是可以用整体二分,还是一样将(>=mid)的数赋作1。

因为这次算的是树上的两点间,所以可以考虑树链剖分(应该是会的吧),重链上的点的dfs序是连续的,用树状数组进行维护,每次加减都是在dfs序上。

对于变化的操作来说,可以在原来的值上(-1),在后来的(+1) 唔,详细的内容还是见代码吧。

注意:

由于存在变化的操作,每一次变化会影响到后面的查询,所以将序列分到左右两组时组内的相对顺序不能发生变化。
从区间到矩阵再到树真是丧心病狂×

#include<bits/stdc++.h>
using namespace std;
#define M 80005
#define lowbit(x) (x&-x)
int num[M],top[M],dep[M],sz[M],son[M],fa[M],n,q,a[M],to[M<<1],pr[M<<1],la[M],tot,Tot,id,ans[M],mi=1e9,mx=0;
bool vis[M];
void add(int x,int y) {
	to[++tot]=y,pr[tot]=la[x],la[x]=tot;
}
struct node {
	int id,l,r,k;
} Q[M*3],B[M*3];
struct Tree {
	int cnt[M];
	void add(int x,int y) {
		while(x<=n)cnt[x]+=y,x+=lowbit(x);
	}
	int sum(int x) {
		int res=0;
		while(x)res+=cnt[x],x-=lowbit(x);
		return res;
	}
} T;
void dfs(int x,int f,int d) {
	dep[x]=d,fa[x]=f,sz[x]=1;
	for(int i=la[x]; i; i=pr[i]) {
		int y=to[i];
		if(y==f)continue;
		dfs(y,x,d+1);
		sz[x]+=sz[y];
		if(sz[y]>sz[son[x]])son[x]=y;
	}
}
void dfs_top(int x,int f,int tp) {
	top[x]=tp,num[x]=++id;
	if(son[x])dfs_top(son[x],x,tp);
	for(int i=la[x]; i; i=pr[i]) {
		int y=to[i];
		if(y==f||y==son[x])continue;
		dfs_top(y,x,y);
	}
}
int get_sum(int x,int y) {//在求lca的过程中算过程,以为重链上的编号是连续的,所以可以直接用树状数组进行维护
	int res=0;
	while(top[x]!=top[y]) {
		if(dep[top[x]]>dep[top[y]])swap(x,y);
		int tp=top[y];
		res+=T.sum(num[y])-T.sum(num[tp]-1);
		y=fa[top[y]];
	}
	if(dep[x]>dep[y])swap(x,y);
	res+=T.sum(num[y])-T.sum(num[x]-1);
	return res;
}
void erfen(int l,int r,int L,int R) {
	if(l>r||L>R)return;
	int l1=L,r1=R,mid=(l+r)>>1;
	for(int i=L; i<=R; i++) {
		if(Q[i].id==0) {
			if(Q[i].r>=mid)T.add(num[Q[i].l],Q[i].k),B[l1++]=Q[i];
			else B[r1--]=Q[i];
		} else {
			int now=get_sum(Q[i].l,Q[i].r);
			if(now>=Q[i].k)B[l1++]=Q[i],ans[Q[i].id]=mid;
			else Q[i].k-=now,B[r1--]=Q[i];
		}
	}
	for(int i=L; i<l1; i++)if(B[i].id==0)T.add(num[B[i].l],-B[i].k);
	for(int i=L; i<l1; i++)Q[i]=B[i];
	int now=l1;
	for(int i=R; i>r1; i--)Q[now++]=B[i];//因为存在变化操作,所以顺序不能改变
	erfen(mid+1,r,L,l1-1);
	erfen(l,mid-1,r1+1,R);
}
int main() {
	Tot=id=tot=0;
	memset(ans,-1,sizeof(ans));
	scanf("%d%d",&n,&q);
	for(int i=1; i<=n; i++)scanf("%d",&a[i]),Q[++Tot]=(node)<%0,i,a[i],1%>,mi=min(mi,a[i]),mx=max(mx,a[i]);
	for(int i=1; i<n; i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	int tot=0;
	dfs(1,0,1),dfs_top(1,0,1);
	for(int i=1; i<=q; i++) {
		int k,x,y;
		scanf("%d%d%d",&k,&x,&y);
		if(!k)Q[++Tot]=(node)<%0,x,a[x],-1%>,Q[++Tot]=(node)<%0,x,y,1%>,a[x]=y,mi=min(mi,y),mx=max(mx,y);//在原来的位置-1,变化后的值+1
		else Q[++Tot]=(node)<%i,x,y,k%>,vis[i]=1;
	}
	erfen(mi,mx,1,Tot);
	for(int i=1; i<=q; i++) {
		if(!vis[i])continue;
		if(ans[i]==-1)printf("invalid request!
");
		else printf("%d
",ans[i]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/cly1231/p/11151671.html