【hihocoder 1723 子树统计】线性基

先说一下自己对线性基及其求法的理解(有误的话请dalao指出鸭OrZ)
线性基,是线代里的那个东西吗?
是也不是,这里的线性基特指在异或运算下的基~
怎么求出来一个线性基呢?
先把线性基的集合设为空集,然后逐个加入元素。
如果待加入元素可以被集合中已有元素通过异或得出,则不加入,否则加入
那么,如何判断待加入元素可不可以被集合中已有元素异或得出呢?
传说中的高斯消元法!
我们可以维护一个对角矩阵(也就是只有主对角线上的元素为1啦),对于每次加入的元素a,进行如下操作:
如果a最高位的1已经有对应的行向量基(啊,我们在这里把一串二进制数看成行向量)了,那就用这个基异或a,重复操作直到a为0(不能插入)或a最高位的1没有对应的行向量,并把a插进去。
之后,要记得用a消去线性基集合中已有的a的最高位对应的位为1的,并消去可以消去的低位的1。

好啦,现在可以看题目啦~

给定一棵N个节点的有根树,树上每个节点有一个非负整数权重Wi。定义节点集合S={i1, i2, ……, ir}的总权重为:(⊕是异或运算)
treecnt1.png
每次询问一棵子树内所有可能出现的总权重数量,即令E为所询问的子树内节点的集合,
treecnt2.png
|T|即为可能出现的总权重数量。
Input
第一行包含两个整数N,Q,表示树的节点数目和询问数目,节点1总是这棵树的根部。
第二行包含N-1个整数,第i个整数Pi表示 i+1 号节点的父亲节点。数据保证PiiP_i≤i
第三行包含N个整数,表示每个节点的权重Wi。
第四行包含Q个整数,每个整数Qi表示询问子树Qi内的可能出现的总权重数量
N100000Q100000Wi260N≤100000,Q≤100000,W_i≤260
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(根节点),就可以获得所有结点的子树线性基啦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章