2017 ACM-ICPC 亚洲区(西安赛区)网络赛 G Xor lca+暴力

2 篇文章 0 订阅


题目链接:https://nanti.jisuanke.com/t/17120

题意:给你一棵树,每个点都有权值,q次询问,每次询问给你a,b,k,问从点a到点b每隔k步的异或和是多少。

思路:现场没敢打,复杂度怎么算都不对。。。后来别人说可以1的情况lca其他暴力,我就试了一下,一开始是TLE,然后我各种迷之优化确实可以过。。。。走k步这里我是用了倍增的数组然后用logk的复杂度走k步。。。然后没用memset。。或者加个输入挂我试过也可以过。最后我还有一步没有优化的就是lca没用rmq,直接用的倍增,已经码得够多了不想码rmq了。下面给代码:

#pragma comment(linker, "/STACK:102400000,102400000") 
#include<iostream>  
#include<cmath>  
#include<queue>  
#include<cstdio>  
#include<queue>  
#include<algorithm>  
#include<cstring>  
#include<string>  
#include<utility>
#include<set>
#include<map>
#include<stack>
#include<vector>
#define maxn 50005
#define inf  0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long LL;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int MAXN = 100005;
const int M = 20;
struct node{
	int v, value, next;
}p[maxn << 1];
int len, head[maxn], dis[maxn], depth[maxn], n, m, father[maxn][25], a[maxn], b[maxn];
void addedge(int u, int v){
	p[len].v = v;
	p[len].next = head[u];
	head[u] = len++;
}
void dfs(int x, int fa){
	father[x][0] = fa;
	b[x] = b[fa] ^ a[x];
	for (int i = head[x]; ~i; i = p[i].next){
		if (p[i].v == fa)
			continue;
		depth[p[i].v] = depth[x] + 1;
		dfs(p[i].v, x);
	}
}
void presolve(){
	dis[1] = depth[1] = 0;
	dfs(1, 0);
	for (int i = 1; i < M; i++)
		for (int j = 1; j <= n; j++)
			father[j][i] = father[father[j][i - 1]][i - 1];
}
int lca(int x, int y){
	if (depth[x] != depth[y]){
		if (depth[x] > depth[y])
			swap(x, y);
		int distant = depth[y] - depth[x];
		for (int i = 0; i < M; i++){
			if (distant&(1 << i))
				y = father[y][i];
		}
	}
	if (x == y)
		return x;
	for (int i = M; i >= 0; i--){
		if (father[x][i] != father[y][i]){
			x = father[x][i];
			y = father[y][i];
		}
	}
	return father[x][0];
}
int find(int x, int k){
	int now = x, tmp = 0;
	while (k){
		if (k & 1)
			now = father[now][tmp];
		k >>= 1;
		tmp++;
	}
	return now;
}
int query(int x, int y, int k){
	if (k == 1)
		return b[x] ^ b[y] ^ a[lca(x, y)];
	else{
		int nowlca = lca(x, y);
		int now = x;
		int ans = 0;
		while (1){
			ans ^= a[now];
			if (depth[now] - k < depth[nowlca])
				break;
			now = find(now, k);
		}
		int ed = depth[nowlca] + k - depth[now] + depth[nowlca];
		if (ed <= depth[y]){
			int tmp = depth[y] - ed;
			now = find(y, tmp%k);
			while (1){
				ans ^= a[now];
				if (depth[now] - k < ed)
					break;
				now = find(now, k);
			}
		}
		return ans;
	}
}
int main(){
	int q;
	while (~scanf("%d%d", &n, &q)){
		for (int i = 1; i <= n; i++)
			head[i] = -1;
		for (int i = 1; i < n; i++){
			int u, v;
			scanf("%d%d", &u, &v);
			addedge(u, v);
			addedge(v, u);
		}
		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		presolve();
		while (q--){
			int u, v, k;
			scanf("%d%d%d", &u, &v, &k);
			printf("%d\n", query(u, v, k));
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值