• BZOJ1146: [CTSC2008]网络管理Network


    BZOJ1146: [CTSC2008]网络管理Network

    Description

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

    Input

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

    Output

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

    Sample Input

    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

    Sample Output

    3
    2
    2
    invalid request!
    题解Here!

    经典的带修树链第k大问题。

    然而一开始敲成了第k小,尴尬。。。

    先说说几种可行的方法:

    1. 大力树套树+树链剖分,复杂度 O(nlog4n) 比较吓人,我一开始打出来就吓一跳,说这怎么可能过?但是真的妥妥的AC 100 ,我也是醉了。。。

    2. 树链剖分+树状数组套主席树,复杂度 O(nlog3n) ,有点吓人。。。

      主席树天生就可以直接求第k大而不用像平衡树那样二分答案。

      看似平衡树可以直接求第k大,实际上这样没法合并多棵平衡树的答案,必须得二分,而主席树不用。

    3. dfs序+树状数组套主席树,复杂度 O(nlog2n) ,能过。

    4. 最后一种,也是最强的一种,复杂度 O(nlogn) ,不过我不会。。。

    这里讲第3种。

    先考虑没有修改怎么做:

    树上第k大,主席树存某节点到根节点的前缀和,查询是这么弄:

    int query(int u,int v,int f,int gf,int k,int l,int r){
        if(l==r)return l;
        int mid=l+r>>1,t=a[a[u].l].sum+a[a[v].l].sum-a[a[f].l].sum-a[a[gf].l].sum;
        if(k<=t)return query(a[u].l,a[v].l,a[f].l,a[gf].l,k,l,mid);
        else return query(a[u].r,a[v].r,a[f].r,a[gf].r,k-t,mid+1,r);
    }
    

    这样就可以了。

    但是有修改,怎么办?

    我们想起序列上的带修改第k大,可以用树状数组套主席树

    那么怎么转换到树上呢?

    树链剖分!

    但是很明显,树剖需要对原树重新编号,会很烦人(我表示我很懒)。

    所以就用了另外一个跟树剖很像但是更简单(功能也更弱)的dfs序

    然后把所有的主席树进行差分,差分后每棵主席树每个位置的值相当于原来它这个位置的值-原来它在dfs序上的前一棵主席树这个位置的值。

    修改时就是把区间修改转化成两个单点修改,查询时一次性查询4条链对应的前缀主席树。

    注:记得开大数组!记得开大数组!记得开大数组!

    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 80010
    using namespace std;
    int n,m,K,c=1,d=0;
    int val[MAXN],num[MAXN<<1],root[MAXN];
    int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],front[MAXN],next[MAXN],top[MAXN];
    struct Tree{
    	int next,to;
    }a[MAXN<<1];
    struct Question{
    	int k,x,y;
    }que[MAXN];
    struct Binary_Indexed_Tree{
    	int size,id[50];
    }bit_one,bit_two;
    inline int read(){
    	int date=0,w=1;char last=0,c=0;
    	while(c<'0'||c>'9'){last=c;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	if(last=='-')w=-1;
    	return date*w;
    }
    namespace ST{
    	int size=0;
    	struct Segment_Tree{
    		int sum,l,r;
    	}a[MAXN*125];
    	inline void buildtree(){
    		a[0].l=a[0].r=a[0].sum=root[0]=0;
    	}
    	void insert(int k,int v,int l,int r,int &rt){
    		if(!rt)rt=++size;
    		a[rt].sum+=v;
    		if(l==r)return;
    		int mid=l+r>>1;
    		if(k<=mid)insert(k,v,l,mid,a[rt].l);
    		else insert(k,v,mid+1,r,a[rt].r);
    	}
    	int query(int l,int r,int k){
    		if(l==r)return l;
    		int mid=l+r>>1,t=0;
    		for(int i=1;i<=bit_one.size;i++)t+=a[a[bit_one.id[i]].l].sum;
    		for(int i=1;i<=bit_two.size;i++)t-=a[a[bit_two.id[i]].l].sum;
    		if(k<=t){
    			for(int i=1;i<=bit_one.size;i++)bit_one.id[i]=a[bit_one.id[i]].l;
    			for(int i=1;i<=bit_two.size;i++)bit_two.id[i]=a[bit_two.id[i]].l;
    			return query(l,mid,k);
    		}
    		else{
    			for(int i=1;i<=bit_one.size;i++)bit_one.id[i]=a[bit_one.id[i]].r;
    			for(int i=1;i<=bit_two.size;i++)bit_two.id[i]=a[bit_two.id[i]].r;
    			return query(mid+1,r,k-t);
    		}
    	}
    }
    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;front[rt]=++d;
    	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;
    		}
    	}
    	next[rt]=d;
    }
    void dfs2(int rt,int f){
    	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);
    	}
    }
    int LCA(int x,int y){
    	while(top[x]!=top[y]){
    		if(deep[top[x]]<deep[top[y]])swap(x,y);
    		x=fa[top[x]];
    	}
    	if(deep[x]>deep[y])swap(x,y);
    	return x;
    }
    inline int lowbit(int x){return x&(-x);}
    inline void update(int x,int v,int w){
    	for(;x<=n;x+=lowbit(x))ST::insert(v,w,1,K,root[x]);
    }
    void work(){
    	int x,y,k;
    	for(int i=1;i<=m;i++){
    		k=que[i].k;x=que[i].x;y=que[i].y;
    		if(k==0){
    			update(front[x],val[x],-1);update(next[x]+1,val[x],1);
    			val[x]=lower_bound(num+1,num+K+1,y)-num;
    			update(front[x],val[x],1);update(next[x]+1,val[x],-1);
    		}
    		else{
    			int lca=LCA(x,y),s=deep[x]+deep[y]-2*deep[lca]+1;
    			if(s<k){
    				printf("invalid request!
    ");
    				continue;
    			}
    			k=s-k+1;
    			bit_one.size=bit_two.size=0;
    			for(int i=front[x];i;i-=lowbit(i))bit_one.id[++bit_one.size]=root[i];
    			for(int i=front[y];i;i-=lowbit(i))bit_one.id[++bit_one.size]=root[i];
    			for(int i=front[lca];i;i-=lowbit(i))bit_two.id[++bit_two.size]=root[i];
    			for(int i=front[fa[lca]];i;i-=lowbit(i))bit_two.id[++bit_two.size]=root[i];
    			printf("%d
    ",num[ST::query(1,K,k)]);
    		}
    	}
    }
    void init(){
    	int x,y;
    	K=n=read();m=read();
    	for(int i=1;i<=n;i++)num[i]=val[i]=read();
    	for(int i=1;i<n;i++){
    		x=read();y=read();
    		add(x,y);
    	}
    	for(int i=1;i<=m;i++){
    		que[i].k=read();que[i].x=read();que[i].y=read();
    		if(que[i].k==0)num[++K]=que[i].y;
    	}
    	sort(num+1,num+K+1);
    	K=unique(num+1,num+K+1)-num-1;
    	for(int i=1;i<=n;i++)val[i]=lower_bound(num+1,num+K+1,val[i])-num;
    	deep[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	ST::buildtree();
    	for(int i=1;i<=n;i++){
    		update(front[i],val[i],1);
    		update(next[i]+1,val[i],-1);
    	}
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    
  • 相关阅读:
    给列表项标记添加自定义图像
    双飞翼布局与圣杯布局
    CSS3 calc()
    CSS滚动视差
    应用层层面面试题汇总
    Linux下OpenSSL 安装
    深入理解:Android 编译系统
    ios 好去处
    IBOutlet & IBAction
    ar技术序章-SDK介绍和选择
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9157655.html
Copyright © 2020-2023  润新知