HDU 6191 01树合并

Query on A Tree

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)
Total Submission(s): 76    Accepted Submission(s): 31


Problem Description
Monkey A lives on a tree, he always plays on this tree.

One day, monkey A learned about one of the bit-operations, xor. He was keen of this interesting operation and wanted to practise it at once.

Monkey A gave a value to each node on the tree. And he was curious about a problem.

The problem is how large the xor result of number x and one node value of label y can be, when giving you a non-negative integer x and a node label u indicates that node y is in the subtree whose root is u(y can be equal to u).

Can you help him?
 

Input
There are no more than 6 test cases.

For each test case there are two positive integers n and q, indicate that the tree has n nodes and you need to answer q queries.

Then two lines follow.

The first line contains n non-negative integers  V1,V2,,Vn , indicating the value of node i.

The second line contains n-1 non-negative integers  F1,F2,Fn1 Fi  means the father of node  i+1 .

And then q lines follow.

In the i-th line, there are two integers u and x, indicating that the node you pick should be in the subtree of u, and x has been described in the problem.

2n,q105

0Vi109

1Fin , the root of the tree is node 1.

1un,0x109
 

Output
For each query, just print an integer in a line indicating the largest result.
 

Sample Input
  
  
2 2 1 2 1 1 3 2 1
 

Sample Output
  
  
2 3
 

Source
 
题意:给出一棵树,每个节点有权值,有q次询问,询问的时候给出一个节点编号 u 和一个值 x ,要求你求出u的子树中与 x 的异或值最大的值。

思路:离线存下询问。每个节点建立一棵01字典树,然后从根节点开始,对于这个节点的询问,去计算最大值,然后把叶子节点与它的父亲节点合并成一个树,然后再做该层的询问。

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
#define maxn 100005

typedef struct tree{
	tree *nxt[2];
}tree;
typedef struct {
	int x; 
	int id;
}res;

tree *root[maxn]; 
vector<int>vec[maxn];
vector<res>Q[maxn];
int val[maxn],ans[maxn];

void Update(tree *q,int a){
	int now;
	tree *p = q;
	for(int i = 29;i >= 0;i--){
		now = 0;
		if(a & (1 << i)) //判断当前位是0还是1 
			now = 1;
		if(p->nxt[now] == NULL){
			tree *tmp = new tree;
			tmp->nxt[0] = tmp->nxt[1] = NULL;
			p->nxt[now] = tmp;
		}
		p = p->nxt[now];
	}
}

int Query(tree *q,int a){
	int now;
	int tmp = 0;
	tree *p = q;
	for(int i = 29;i >= 0;i--){
		now = 0;
		if(a & (1 << i)) //判断当前为 
			now = 1;
		if(p->nxt[now ^ 1] != NULL){	// now ^ 1 = 0 则 now 为 1 ,它最佳匹配是 0 ,这里贪心 
			p = p->nxt[now ^ 1];
			tmp |= (1 << i);		//贪心的结果就是使它成为 1  
		}else{
			p = p->nxt[now];	
		}
	}
	return tmp;
}

tree* Merge(tree *t1,tree *t2){
	if(t1 == NULL)
		return t2;
	if(t2 == NULL)
		return t1;
	t1->nxt[0] = Merge(t1->nxt[0],t2->nxt[0]);
	t1->nxt[1] = Merge(t1->nxt[1],t2->nxt[1]);
	free(t2);
	return t1;
}

void Delete(tree *q){
	if(q->nxt[0])
		Delete(q->nxt[0]);
	if(q->nxt[1])
		Delete(q->nxt[1]);
	free(q);
}

void Run(int u){
	int v;
	root[u] = new tree;		//建树 
	root[u]->nxt[1] = root[u]->nxt[0] = NULL;
	Update(root[u],val[u]);		//上传树的指针和值,从根节点开始 
	printf("u = %d  val[u] = %d\n",u,val[u]); 
	for(int i = 0;i < vec[u].size();i++){
		v = vec[u][i];	//找到它的 儿子 
		Run(v);			//儿子建树 
		root[u] = Merge(root[u],root[v]);	// 合并 u 和 它的子树 
	}
	// 从叶子节点开始 
	for(int i = 0;i < Q[u].size();i++){		//对于 u 层的询问,做计算,存答案  ,计算完就与根节点合并 
		ans[Q[u][i].id] = Query(root[u],Q[u][i].x);
	}
		
}
int main(){
	int n,q,x,y;
	res tmp;
	while(scanf("%d %d",&n,&q) != EOF){
		memset(ans,0,sizeof(ans));
		for(int i = 1;i <= n;i++){
			Q[i].clear();
			vec[i].clear();
			scanf("%d",&val[i]);
		}
		for(int i = 2;i <= n;i++){
			scanf("%d",&x);
			vec[x].push_back(i);	// vec[x] 存的是 x 的儿子 
		}
		for(int i = 1;i <= q;i++){
			scanf("%d %d",&y,&x);
			tmp.x = x; 			//第 y 层的查询 
			tmp.id = i;
			Q[y].push_back(tmp);
		}	
		Run(1);
		for(int i = 1;i <= q;i++)
			printf("%d\n",ans[i]);
		Delete(root[1]);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值