先说一下自己对线性基及其求法的理解(有误的话请dalao指出鸭OrZ)
线性基,是线代里的那个东西吗?
是也不是,这里的线性基特指在异或运算下的基~
怎么求出来一个线性基呢?
先把线性基的集合设为空集,然后逐个加入元素。
如果待加入元素可以被集合中已有元素通过异或得出,则不加入,否则加入。
那么,如何判断待加入元素可不可以被集合中已有元素异或得出呢?
传说中的高斯消元法!
我们可以维护一个对角矩阵(也就是只有主对角线上的元素为1啦),对于每次加入的元素a,进行如下操作:
如果a最高位的1已经有对应的行向量基(啊,我们在这里把一串二进制数看成行向量)了,那就用这个基异或a,重复操作直到a为0(不能插入)或a最高位的1没有对应的行向量,并把a插进去。
之后,要记得用a消去线性基集合中已有的a的最高位对应的位为1的,并消去可以消去的低位的1。
好啦,现在可以看题目啦~
给定一棵N个节点的有根树,树上每个节点有一个非负整数权重Wi。定义节点集合S={i1, i2, ……, ir}的总权重为:(⊕是异或运算)
每次询问一棵子树内所有可能出现的总权重数量,即令E为所询问的子树内节点的集合,
|T|即为可能出现的总权重数量。
Input
第一行包含两个整数N,Q,表示树的节点数目和询问数目,节点1总是这棵树的根部。
第二行包含N-1个整数,第i个整数Pi表示 i+1 号节点的父亲节点。数据保证。
第三行包含N个整数,表示每个节点的权重Wi。
第四行包含Q个整数,每个整数Qi表示询问子树Qi内的可能出现的总权重数量
Output
输出共Q行,每行包含一个整数T表示子树Qi内可能出现的总权重数量
Sample Input
7 3
1 2 2 1 5 5
8 4 3 1 2 4 6
2 5 1
Sample Output
8
4
16
大概就是给一棵树,然后每次给一个q,问以q为根的各种树的节点异或和结果有多少种。
预处理出来每个根的线性基的大小m,答案就是 2m。
怎么预处理呢?
struct LineBasis{
LL b[66];
LL p[66];
int cnt;
LineBasis(){
memset(b,0,sizeof(b));
memset(p,0,sizeof(p));
cnt = 0;
}
void Insert(LL val){
for(int i=60;i>=0;i--){
if((1LL<<i)&val){
if(b[i] == 0){
b[i] = val;
break;
}
val ^= b[i];
}
}
if(val > 0)
cnt++;
}
LineBasis Merge(LineBasis n1,LineBasis n2){
LineBasis ret = n1;
for(int i=0;i<=60;i++)
if(n2.b[i])
ret.Insert(n2.b[i]);
return ret;
}
};
代码都来自SSimpLe_Y ~
看起来是不是非常正确,然后你需要一个dfs来调用这个东西~
void dfs(int x){
for(int i=0;i<v[x].size();i++){
int xx = v[x][i];
dfs(xx);
num[x] = num[x].Merge(num[x],num[xx]);
}
num[x].Insert(a[x]);
}
这样,只需要dfs(根节点),就可以获得所有结点的子树线性基啦!