题干:

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

给定一棵N个节点的有根树,树上每个节点有一个非负整数权重Wi。定义节点集合S={i1, i2, ……, ir}的总权重为:(⊕是异或运算)

每次询问一棵子树内所有可能出现的总权重数量,即令E为所询问的子树内节点的集合,

|T|即为可能出现的总权重数量。

输入

第一行包含两个整数N,Q,表示树的节点数目和询问数目,节点1总是这棵树的根部。

第二行包含N-1个整数,第i个整数P_i表示i+1号节点的父亲节点。数据保证Pi≤i。

第三行包含N个整数,表示每个节点的权重Wi。

第四行包含Q个整数,每个整数Qi表示询问子树Qi内的可能出现的总权重数量

N≤100000,Q≤100000,Wi≤260

输出

输出共Q行,每行包含一个整数T表示子树Qi内可能出现的总权重数量

样例输入

7 3
1 2 2 1 5 5
8 4 3 1 2 4 6
2 5 1

样例输出

8
4
16

 

解题报告:

    dfs一遍求出以每个节点为根节点的线性基大小,设为sz,那么2^sz就是当前查询的答案。因为查询次数不多,里面可以带个log,所以不需要提前预处理出来。每次直接计算sz的大小就行了。往前回溯的时候顺便给合并这两个线性基。

AC代码:(451ms)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
int n,q,fa[MAX];
vector<int> vv[MAX];
const int Bit = 64;
ll Base[MAX][66];
ll w[MAX],db[123];
void insert(int cur,ll x) {
	for(int j = Bit-1; j>=0; j--) {
		if(x>>j & 1) {
			if(Base[cur][j] == 0) {
				Base[cur][j] = x;break;
			}
			else x ^= Base[cur][j];
		}
	}
}
void merge(int cur,int rt) {
	for(int j = Bit-1; j>=0; j--) {
		if(Base[cur][j]) {
			insert(rt,Base[cur][j]);
		}
	}
} 
void dfs(int cur,int rt) {
	int up = vv[cur].size();
	for(int i = 0; i<up; i++) {
		int v = vv[cur][i];
		if(v == rt) continue;
		dfs(v,cur);
		merge(v,cur);
	}
	insert(cur,w[cur]);
}
int main()
{
	db[0] = 1;
	for(int i = 1; i<=66; i++) db[i] = db[i-1]*2;
	cin>>n>>q;
	for(int i = 2; i<=n; i++) {
		scanf("%d",&fa[i]);
		vv[fa[i]].pb(i);
	}
	for(int i = 1; i<=n; i++) scanf("%lld",w+i);
	dfs(1,0);
	while(q--) {
		int x;
		scanf("%d",&x);
		int sz = 0;
		for(int j = 0; j<=Bit-1; j++) {
			if(Base[x][j]) sz++;
		}
		printf("%lld\n",db[sz]);
	}
	return 0 ;
 }