【BZOJ1146】网络管理(CTSC2008)-树状数组+主席树

测试地址:网络管理
做法: 本题需要用到树状数组+主席树。
经典的带修改树上路径第 k k k大问题,不过我太菜了居然忘了有树上主席树这个东西…
不带修改的话能用树上主席树做,那么带修改怎么办呢?因为一次修改会影响一棵子树上的所有线段树,所以我们还是把树拍成DFS序,就变成区间修改了。注意到查询的时候只是查单棵线段树,因此可以运用差分的方法,把上述操作变成单点修改,前缀和求和,这个就用树状数组套主席树(准确上来讲算树状数组套动态开点线段树?)就可以统计修改的贡献了(即变成单次修改/查询 log ⁡ n \log n logn棵线段树)。注意原来点的贡献和修改的贡献是在不同的结构上统计的,一个是在树上主席树上统计,一个是在树状数组套线段树上统计。在二分的时候,把所有带贡献的线段树点列出来,每次算贡献的时候都扫一遍,向下走的时候所有点一起走即可。这样我们就以 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)的时间复杂度解决了这个问题。
写起来挺复杂,但也不是太难,居然1A了,信心获得增长。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,q,m,tot=0,val[100010],tote=0,first[100010]={0};
int pos[200010],k[100010],a[100010],b[100010];
int fa[100010][20]={0},dep[100010]={0},in[100010],out[100010],tim=0;
int seg[10000010]={0},ch[10000010][2]={0},rt[200010]={0};
int totp,p[510],type[510];
struct edge
{
	int v,next;
}e[200010];
struct forsort
{
	bool type;
	int id,val;
}f[200010];

void insert(int a,int b)
{
	e[++tote].v=b;
	e[tote].next=first[a];
	first[a]=tote;
}

void pushup(int v)
{
	seg[v]=seg[ch[v][0]]+seg[ch[v][1]];
}

void insert(int &v,int last,int l,int r,int x)
{
	v=++tot;
	seg[v]=seg[last];
	ch[v][0]=ch[last][0];
	ch[v][1]=ch[last][1];
	if (l==r) {seg[v]++;return;}
	int mid=(l+r)>>1;
	if (x<=mid) insert(ch[v][0],ch[last][0],l,mid,x);
	else insert(ch[v][1],ch[last][1],mid+1,r,x);
	pushup(v);
}

void modify(int &v,int l,int r,int x,int d)
{
	if (!v) v=++tot;
	if (l==r) {seg[v]+=d;return;}
	int mid=(l+r)>>1;
	if (x<=mid) modify(ch[v][0],l,mid,x,d);
	else modify(ch[v][1],mid+1,r,x,d);
	pushup(v);
}

void dfs(int v)
{
	in[v]=++tim;
	insert(rt[v],rt[fa[v][0]],1,m,val[v]);
	for(int i=first[v];i;i=e[i].next)
		if (e[i].v!=fa[v][0])
		{
			fa[e[i].v][0]=v;
			dep[e[i].v]=dep[v]+1;
			dfs(e[i].v);
		}
	out[v]=tim;
}

int lowbit(int x)
{
	return x&(-x);
}

void Add(int v,int x,int d)
{
	for(int i=v;i<=n;i+=lowbit(i))
		modify(rt[n+i],1,m,x,d);
}

void insert_p(int v,int d)
{
	if (!v) return;
	p[++totp]=v,type[totp]=d;
}

void Sum(int v,int d)
{
	for(int i=v;i;i-=lowbit(i))
		insert_p(rt[n+i],d);
}

void query(int k)
{
	int l=1,r=m,s,mid;
	while(l!=r)
	{
		s=0,mid=(l+r)>>1;
		for(int i=1;i<=totp;i++)
			s+=seg[ch[p[i]][1]]*type[i];
		if (s<k)
		{
			r=mid;
			for(int i=1;i<=totp;i++)
				p[i]=ch[p[i]][0];
			k-=s;
		}
		else
		{
			l=mid+1;
			for(int i=1;i<=totp;i++)
				p[i]=ch[p[i]][1];
		}
	}
	s=0;
	for(int i=1;i<=totp;i++)
		s+=seg[p[i]]*type[i];
	if (s<k) printf("invalid request!\n");
	else printf("%d\n",pos[l]);
}

bool cmp(forsort a,forsort b)
{
	return a.val<b.val;
}

int lca(int a,int b)
{
	if (dep[a]<dep[b]) swap(a,b);
	for(int i=18;i>=0;i--)
		if (dep[fa[a][i]]>=dep[b])
			a=fa[a][i];
	if (a==b) return a;
	for(int i=18;i>=0;i--)
		if (fa[a][i]!=fa[b][i])
			a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}

int main()
{
	scanf("%d%d",&n,&q);
	
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&f[++tot].val);
		f[tot].type=0,f[tot].id=i;
	}
	for(int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		insert(a,b),insert(b,a);
	}
	
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&k[i],&a[i],&b[i]);
		if (!k[i])
		{
			f[++tot].type=1,f[tot].id=i;
			f[tot].val=b[i];
		}
	}
	
	sort(f+1,f+tot+1,cmp);
	m=0;
	for(int i=1;i<=tot;i++)
	{
		if (i==1||f[i].val!=f[i-1].val) pos[++m]=f[i].val;
		if (f[i].type) b[f[i].id]=m;
		else val[f[i].id]=m;
	}
	
	tot=0;
	dep[1]=1;
	dfs(1);
	for(int i=1;i<=18;i++)
		for(int j=1;j<=n;j++)
			fa[j][i]=fa[fa[j][i-1]][i-1];
	
	for(int i=1;i<=q;i++)
	{
		if (!k[i])
		{
			Add(in[a[i]],val[a[i]],-1);
			Add(out[a[i]]+1,val[a[i]],1);
			val[a[i]]=b[i];
			Add(in[a[i]],val[a[i]],1);
			Add(out[a[i]]+1,val[a[i]],-1);
		}
		else
		{
			totp=0;
			insert_p(rt[a[i]],1);
			insert_p(rt[b[i]],1);
			int g=lca(a[i],b[i]);
			insert_p(rt[g],-1);
			if (fa[g][0]) insert_p(rt[fa[g][0]],-1);
			Sum(in[a[i]],1),Sum(in[b[i]],1),Sum(in[g],-1);
			if (fa[g][0]) Sum(in[fa[g][0]],-1);
			query(k[i]);
		}
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值