先說一下自己對線性基及其求法的理解(有誤的話請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(根節點),就可以獲得所有結點的子樹線性基啦!