河南理工大学新生挑战赛 C.Xor Path

题目链接

题意:给你一棵树,每个节点都有权值,有m次查询,问u节点到v节点的最短路径上所有点的异或值。

思路:异或有一个这样的性质就是,异或一个数两次相当于没有异或,所以我们设dp【i】为根节点到i节点的异或值,那么dp【i】=  dp【f【i】】^ w【i】,那么要求的就是答案就等于,dp【u】^   dp【v】^  lca(u,v),倍增求lca的步骤就是,如果两个节点的深度不一致,让深度大的节点跳到跟深度小的节点深度相同的祖先,如果相等就返回,如果不相等就同步往上跳。

好吧,我之前搞错了,树链剖分是求LCA最省时间,并且最省空间的方法。我之所以被卡,是因为我套了线段树,下面附上用树链剖分求LCA的代码。

倍增: 

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5+100;
int a[maxn],d[maxn],dep[maxn];
int f[maxn][20];
vector<int>G[maxn];
void dfs(int u,int fa)
{
	f[u][0] = fa;
	dep[u] = dep[fa] + 1;
	for(int i=1;i<=19;i++)f[u][i] = f[f[u][i-1]][i-1];
	for(auto v : G[u])
	{
		if(v == fa)continue;
		d[v] = d[u]^a[v];
		dfs(v,u);
	}
}

int lca(int u,int v)
{
	if(dep[u] < dep[v])swap(u,v);

	for(int i=19;i>=0;i--)
		if(dep[f[u][i]] >= dep[v])u = f[u][i];

	if(u == v)return u;

	for(int i=19;i>=0;i--)
	if(f[u][i] != f[v][i])u=f[u][i],v=f[v][i];

	return f[u][0];

}
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	d[1] = a[1];
	dfs(1,0);
	int m;
	cin>>m;
	while(m--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		int ans = d[u]^d[v]^a[lca(u,v)];
		cout<<ans<<'\n';
	}
}

树链剖分:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5+100;
int n,a[maxn];
int sz[maxn],f[maxn],dep[maxn],d[maxn],son[maxn];
int top[maxn];
vector<int>G[maxn];
void dfs1(int u,int fa)
{
	f[u] = fa;
	dep[u] = dep[fa] + 1;
	sz[u] = 1;
	for(auto v : G[u])
	{
		if(v == fa)continue;
		d[v] = d[u]^a[v];
		dfs1(v,u);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]])son[u] = v;
	}
}

void dfs(int u,int root)
{
	top[u] = root;
	if(son[u])dfs(son[u],root);
	for(auto v : G[u])
	{
		if(v == f[u] || v == son[u])continue;
		dfs(v,v);
	}
}
int LCA(int u,int v)
{
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]])swap(u,v);
		u = f[top[u]];
	}
	if(dep[u] > dep[v])swap(u,v);
	return u;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	d[1] = a[1];
	dfs1(1,0);
	dfs(1,1);
	int m;
	scanf("%d",&m);
	while(m--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		int ans = d[u]^d[v]^a[LCA(u,v)];
		printf("%d\n",ans);
	}
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值