码迷,mamicode.com
首页 > Web开发 > 详细

[BZOJ 1146]网络管理Network 树上带修改路径k值

时间:2017-12-07 21:01:24      阅读:229      评论:0      收藏:0      [点我收藏+]

标签:就是   scan   差分   cto   主席树   efi   它的   数组   时间复杂度   

题目意思非常清楚,就是要求树上带修改的路径k大值

如果不带修改的话,我会用树上主席树去搞,以父子关系建树,可以参见 [BZOJ 3123]森林

但是带修改就不会打了QAQ,于是去学了另一种在dfs序上搞的方法(同时感谢呵呵酵母菌的帮助)

其实思想是一样的,就是搞出来节点到根路径的线段树,然后用x+y-lca-fa(lca)去差分出来树上路径的线段树,再去里面查询k值

技术分享图片

那么我们怎么得到点到根路径的线段树呢?可以在dfs序上差分啊!

dfs序列上的dfnl[x]~dfnr[x]包含的是以x为根节点的子树的dfs序(这个学过dfs序的应该都知道)

对于一个节点x,它的值只会对于它的子树内的点造成贡献,于是我们在dfs序上dfnl[x]的位置+1,dfnr[x]+1的位置-1,这样我们求前缀和的时候就会把这个子树内的值给差分掉

意思就是说,求x到root的路径线段树,我们求dfnl[x]的前缀和线段树就好了,因为这个路径之外的子树或节点,要么在dfnl[x]的后边,要么就被前缀和差分掉了

因为带修改,所以用树状数组维护。然后我就没有打主席树,而是打树状数组套动态开点线段树(相当于直接把线段树类比数组来用了)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 101000
#define lc(x) (tree[x].lc)
#define rc(x) (tree[x].rc)
#include<vector>
using namespace std;
vector<int> b;
int lowbit(int x){return x&(-x);}
int findx(int x){
	return lower_bound(b.begin(),b.end(),x)-b.begin()+1;
}
int n,q,a[N],root[N],size,len;
int p[N][20],dep[N],fa[N];
struct haha{
	int lc,rc,sum;
}tree[N*100];
struct xixi{
	int next,to;
}edge[N*2];
int head[N],cnt=1;
void add2(int u,int v){
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
}
int dfnl[N],dfnr[N],ji;
void dfs(int x){
	dfnl[x]=++ji;
	for(int i=head[x];i;i=edge[i].next){
		int to=edge[i].to;
		if(to!=fa[x]){
			fa[to]=x;
			dep[to]=dep[x]+1;
			dfs(to);
		}
	}
	dfnr[x]=ji;
}
void init(){
	for(int j=0;(1<<j)<=n;j++) pos(i,1,n) p[i][j]=-1;
	pos(i,1,n) p[i][0]=fa[i];
	for(int j=1;(1<<j)<=n;j++){
		pos(i,1,n){
			if(p[i][j-1]!=-1){
				p[i][j]=p[p[i][j-1]][j-1];
			}
		}
	}
}
int lca(int a,int b){
	if(dep[a]<dep[b]) swap(a,b);
	int i;for(i=0;(1<<i)<=dep[a];i++);i--;
	for(int j=i;j>=0;j--){
		if(dep[a]-(1<<j)>=dep[b]) a=p[a][j];
	}
	if(a==b) return a;
	for(int j=i;j>=0;j--){
		if(p[a][j]!=p[b][j]&&p[a][j]!=-1){
			a=p[a][j];b=p[b][j];
		}
	}
	return fa[a];
}
struct nono{
	int k,a,b;
}ques[N];
void update(int &rt,int l,int r,int pos,int num){
	if(!rt) rt=++size;
	tree[rt].sum+=num;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid) update(lc(rt),l,mid,pos,num);
	else update(rc(rt),mid+1,r,pos,num);
	tree[rt].sum=tree[lc(rt)].sum+tree[rc(rt)].sum;
}
void add(int x,int pos,int num){
	while(x<=n){
		update(root[x],1,len,pos,num);
		x+=lowbit(x);
	}
}
int cunx[200],cuny[200],cunlca[200],cunfalca[200];
int query(int l,int r,int k){
	if(l==r) return l;
	int sumx(0),sumy(0),sumlca(0),sumfalca(0);
	pos(i,1,cunx[0]){
		sumx+=tree[lc(cunx[i])].sum;
	}
	pos(i,1,cuny[0]){
		sumy+=tree[lc(cuny[i])].sum;
	}
	pos(i,1,cunlca[0]){
		sumlca+=tree[lc(cunlca[i])].sum;
	}
	pos(i,1,cunfalca[0]){
		sumfalca+=tree[lc(cunfalca[i])].sum;
	}
	int mid=(l+r)>>1;
	int temp=sumx+sumy-sumlca-sumfalca;
	if(temp>=k){
		pos(i,1,cunx[0]){
			cunx[i]=lc(cunx[i]);
		}
		pos(i,1,cuny[0]){
			cuny[i]=lc(cuny[i]);
		}
		pos(i,1,cunlca[0]){
			cunlca[i]=lc(cunlca[i]);
		}
		pos(i,1,cunfalca[0]){
			cunfalca[i]=lc(cunfalca[i]);
		}
		return query(l,mid,k);	
	} 
	else{
		pos(i,1,cunx[0]){
			cunx[i]=rc(cunx[i]);
		}
		pos(i,1,cuny[0]){
			cuny[i]=rc(cuny[i]);
		}
		pos(i,1,cunlca[0]){
			cunlca[i]=rc(cunlca[i]);
		}
		pos(i,1,cunfalca[0]){
			cunfalca[i]=rc(cunfalca[i]);
		}
		return query(mid+1,r,k-temp);
	} 
}
int la;
int Query(int x,int y,int k){
	int falc=fa[la];
	cunx[0]=cuny[0]=cunlca[0]=cunfalca[0]=0;
	int tx=dfnl[x],ty=dfnl[y],tlca=dfnl[la],tfalc=dfnl[falc];
	while(tx>0){
		cunx[++cunx[0]]=root[tx];
		tx-=lowbit(tx);
	}
	while(ty>0){
		cuny[++cuny[0]]=root[ty];
		ty-=lowbit(ty);
	}
	while(tlca>0){
		cunlca[++cunlca[0]]=root[tlca];
		tlca-=lowbit(tlca);
	}
	while(tfalc>0){
		cunfalca[++cunfalca[0]]=root[tfalc];
		tfalc-=lowbit(tfalc);
	}
	return query(1,len,k);
}
int main(){
	scanf("%d%d",&n,&q);len=n+q;
	pos(i,1,n) scanf("%d",&a[i]),b.push_back(a[i]);
	pos(i,1,n-1){
		int x,y;scanf("%d%d",&x,&y);
		add2(x,y);add2(y,x);
	}
	pos(i,1,q){
		scanf("%d%d%d",&ques[i].k,&ques[i].a,&ques[i].b);
		if(ques[i].k==0){
			b.push_back(ques[i].b);
		}
	}
	sort(b.begin(),b.end());
	b.erase(unique(b.begin(),b.end()),b.end());
	dfs(1);
	init();
	pos(i,1,n){
		add(dfnl[i],findx(a[i]),1);
		add(dfnr[i]+1,findx(a[i]),-1);
	}
	pos(i,1,q){
		int k=ques[i].k,x=ques[i].a,y=ques[i].b;
		if(!k){
			add(dfnl[x],findx(a[x]),-1);
			add(dfnr[x]+1,findx(a[x]),1);
			add(dfnl[x],findx(y),1);
			add(dfnr[x]+1,findx(y),-1);
			a[x]=y;
		}
		else{
			la=lca(x,y);
			int jishu=dep[x]+dep[y]-2*dep[la]+1;
			if(jishu<k) printf("invalid request!\n");
			else printf("%d\n",b[Query(x,y,jishu-k+1)-1]);
		}
	}
	return 0;
}  

感悟:

这道题为我提供了一个新的思路,就是可以把树上的东西搞到dfs序上(原来也一直知道但是从来没打过T-T),因为在序列上可以用高级数据结构搞事

在dfs序上差分,求出节点到root想要的答案,然后再在树上差分。妙啊!

比如说最简单的,求带修改树上路径长度,我们可以用这种方法代替树链剖分(时间复杂度理论分析O(nlogn),似乎要快啊OvO)

嗯。学习到了。

[BZOJ 1146]网络管理Network 树上带修改路径k值

标签:就是   scan   差分   cto   主席树   efi   它的   数组   时间复杂度   

原文地址:http://www.cnblogs.com/Hallmeow/p/8000316.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!