hdu 5274(树链剖分)

本文探讨了一道使用树链剖分解决点权问题的算法题,详细阐述了题目的背景、解题思路,并分析了作者在实现过程中遇到的问题与解决方案。文章深入解析了树链剖分的基本概念及其在点权问题中的应用,同时提供了代码实现的细节,对于理解并解决类似问题具有较高的参考价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解题思路:这道题据说是树链剖分,所以也学习了一下。

http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

不同的是这里是点权值,我按照相似的处理方式,不知道为什么WA了。。。调了好久。。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100005;
struct Segment
{
	int l,r,sum;
}tree[maxn<<2];
struct Edge
{
	int to,next;
}edge[maxn<<1];
int A[maxn],son[maxn],top[maxn],w[maxn];
int dep[maxn],fa[maxn],size[maxn];
int cnt,num,pre[maxn];
int n,q,idx[maxn];

void addedge(int u,int v)
{
	edge[cnt].to = v;
	edge[cnt].next = pre[u];
	pre[u] = cnt++;
}

void dfs(int u)
{
	size[u] = 1;
	son[u] = 0;
	int tmp = 0;
	for(int i = pre[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(fa[u] == v) continue;
		fa[v] = u;
		dep[v] = dep[u] + 1;
		dfs(v);
		size[u] += size[v];
		if(size[v] > tmp)
		{
			tmp = size[v];
			son[u] = v;
		}
	}
}

void build_tree(int u,int tp)
{
	w[u] = ++num; top[u] = tp; idx[num] = u;
	if(son[u] != 0) build_tree(son[u],tp);
	for(int i = pre[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(v == son[u] || fa[u] == v) continue;
		build_tree(v,v);
	}
}

void build_Segment(int rt,int l,int r)
{
	tree[rt].l = l, tree[rt].r = r;
	tree[rt].sum = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build_Segment(rt<<1,l,mid);
	build_Segment(rt<<1|1,mid+1,r);
}

void update(int rt,int pos,int val)
{
	if(tree[rt].l == tree[rt].r)
	{
		tree[rt].sum = val;
		return;
	}
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(pos <= mid) update(rt<<1,pos,val);
	else update(rt<<1|1,pos,val);
	tree[rt].sum = tree[rt<<1].sum ^ tree[rt<<1|1].sum;
}

int query(int rt,int l,int r)
{
	if(l <= tree[rt].l && tree[rt].r <= r)
		return tree[rt].sum;
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	int ans = 0;
	if(l <= mid) ans ^= query(rt<<1,l,r);
	if(mid < r) ans ^= query(rt<<1|1,l,r);
	return ans;
}

int find(int l,int r)
{
	int f1 = top[l], f2 = top[r];
	int ans = 0;
	while(f1 != f2)
	{
		if(dep[f1] < dep[f2])
		{
			swap(f1,f2);
			swap(l,r);
		}
		ans ^= query(1,w[f1],w[l]);
		l = fa[f1], f1 = top[l];
	}
	if(l == r) return ans^A[f1];
	if(dep[l] > dep[r]) swap(l,r);
	return ans ^= query(1,w[son[l]],w[r])^A[f1];
}

int main()
{
	int t,u,v,op;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		memset(pre,-1,sizeof(pre));
		cnt = num = 0;
		for(int i = 1; i < n; i++)
		{
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		dep[1] = 0;
		dfs(1);
		build_tree(1,1);
		build_Segment(1,1,num);
		for(int i = 1; i <= n; i++)
		{
			scanf("%d",&A[i]);
			A[i]++;
			update(1,w[i],A[i]);
		}
		for(int i = 1; i <= q; i++)
		{
			scanf("%d%d%d",&op,&u,&v);
			if(op == 0)
			{
				A[u] = v + 1;
				update(1,w[u],A[u]);
			}
			else 
			{
				int ans = find(u,v);
				printf("%d\n",ans-1);
			}
		}
	}
	return 0;
}




PS:今天早上把这道题给A了,参考了一下别人的代码,发现自己的代码不一样的地方就是在find函数里,我是按照边更新的方式去写的,而这道题是点更新。关键是最后f1=f2后,如何把根节点加入进去。但仔细想想还是没明白我的为什么错了。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 100005;
struct Segment
{
	int l,r,sum;
}tree[maxn<<2];
struct Edge
{
	int to,next;
}edge[maxn<<1];
int A[maxn],son[maxn],top[maxn],w[maxn];
int dep[maxn],fa[maxn],size[maxn];
int cnt,num,pre[maxn];
int n,q;

void addedge(int u,int v)
{
	edge[cnt].to = v;
	edge[cnt].next = pre[u];
	pre[u] = cnt++;
}

void dfs(int u)
{
	size[u] = 1;
	son[u] = 0;
	int tmp = 0;
	for(int i = pre[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(fa[u] == v) continue;
		fa[v] = u;
		dep[v] = dep[u] + 1;
		dfs(v);
		size[u] += size[v];
		if(size[v] > tmp)
		{
			tmp = size[v];
			son[u] = v;
		}
	}
}

void build_tree(int u,int tp)
{
	w[u] = ++num; top[u] = tp;
	if(son[u] != 0) build_tree(son[u],tp);
	for(int i = pre[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(v == son[u] || fa[u] == v) continue;
		build_tree(v,v);
	}
}

void build_Segment(int rt,int l,int r)
{
	tree[rt].l = l, tree[rt].r = r;
	tree[rt].sum = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build_Segment(rt<<1,l,mid);
	build_Segment(rt<<1|1,mid+1,r);
}

void update(int rt,int pos,int val)
{
	if(tree[rt].l == tree[rt].r)
	{
		tree[rt].sum = val;
		return;
	}
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	if(pos <= mid) update(rt<<1,pos,val);
	else update(rt<<1|1,pos,val);
	tree[rt].sum = tree[rt<<1].sum ^ tree[rt<<1|1].sum;
}

int query(int rt,int l,int r)
{
	if(l <= tree[rt].l && tree[rt].r <= r)
		return tree[rt].sum;
	int mid = (tree[rt].l + tree[rt].r) >> 1;
	int ans = 0;
	if(l <= mid) ans ^= query(rt<<1,l,r);
	if(mid < r) ans ^= query(rt<<1|1,l,r);
	return ans;
}

int find(int l,int r)
{
	int f1 = top[l], f2 = top[r];
	int ans = 0;
	while(f1 != f2)
	{
		if(dep[f1] < dep[f2])
		{
			swap(f1,f2);
			swap(l,r);
		}
		ans ^= query(1,w[f1],w[l]);
		l = fa[f1], f1 = top[l];
	}
	if(dep[l] > dep[r]) swap(l,r);<span style="white-space:pre">	</span>//没有比较l与r,这是关键点
	return ans ^= query(1,w[l],w[r]);
}

int main()
{
	int t,u,v,op;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		memset(pre,-1,sizeof(pre));
		cnt = num = 0;
		for(int i = 1; i < n; i++)
		{
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		dep[1] = 0;
		dfs(1);
		build_tree(1,1);
		build_Segment(1,1,num);
		for(int i = 1; i <= n; i++)
		{
			scanf("%d",&A[i]);
			update(1,w[i],A[i]+1);
		}
		for(int i = 1; i <= q; i++)
		{
			scanf("%d%d%d",&op,&u,&v);
			if(op == 0)
				update(1,w[u],v+1);
			else 
			{
				int ans = find(u,v);
				printf("%d\n",ans-1);
			}
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值