有关trie树的一道作业

这是本蒟蒻的第一篇题解,有不足之处还请多多包涵(๑•̀ㅂ•́)و✧

先上题目

题目描述

\(A\) 国有 \(n\) 个主要城市,这 \(n\) 个主要城市通过 \(n-1\) 条双向道路联通,每条道路会有一个愉悦值 \(w\)\(A\) 国除了有这 \(n-1\) 条道路外,在每两个城市之间,还会有一条双向传送道,每条传送道的愉悦值为 \(0\)

\(Z\) 来到了 \(A\) 国,他决定对 \(A\) 国进行若干次游历,每次游历会从给定的起点走向给定的终点。小 \(Z\) 除了可以走普通的道路之外,还可以走传送通道。可惜传送道的费用太高昂了,小 \(Z\) 决定,每次游历最多只经过一次传送道。

随着时间的推移,\(Z\) 国的这 \(n-1\) 条道路也在不断地完善,因此有时候,所有道路的愉悦值会同时异或上一个值 \(v\)

\(Z\) 定义,一次游历的愉悦值,为这次游历中小 \(Z\) 经过的所有边的愉悦值的异或和。他希望你告诉他,每一次游历的愉悦值的最大值是多少?

你需要注意的是,一次游历的路线可以经过每个点或者每条边任意多次,但必须从起点走到终点,且最多经过一次传送道;起点和终点可能存在重合的情况;一次游历可以中途经过终点,但不结束这次游历。

输入格式

第一行两个整数 \(n,m\),表示 \(A\) 国城市的个数和小 \(Z\) 的游历次数。

接下来 \(Z-1\) 行,每行三个整数 \(x,y,w\),表示城市 \(x\) 与城市 \(y\) 之间有一条愉悦度为 \(w\) 的道路。

接下来的 \(m\) 行,每行先是一个整数 \(opt\)。如果 \(opt=1\),接下来两个正整数 \(x,y\),表示小 \(Z\) 将进行一次从 \(x\) 出发到 \(y\) 的游历;如果 \(opt=2\),接下来一个整数 \(v\),表示所有道路的愉悦值异或上一个值 \(v\)

输出格式

对于每次询问,输出一行表示这次游历的最大的愉悦值。

样例输入

6 4
1 2 3
1 3 0
2 4 2
2 5 4
3 6 1
1 4 1
1 2 6
2 4
1 2 6

样例输出

7
6
7

数据范围

对于 \(100\) % 的数据,满足 \(1 \leq n, m \leq 5\) \(\times 10^5\) ,\(0 \leq w,v < 2^{12}\) , \(1 \leq x, y \leq n\)

 

题解

  一看到这题疯狂的异或,我们就想到了肯定要用 \(trie\) 树去做他(其实是因为上课刚讲的)。那么回归这题。先看题意,本质上讲,基本是肯定要走一次传送带的(不然他给你干嘛。。。),记起始端点\(A,B\) ,中间端点\(C,D\)。那么我们实际上就是从\(A\)\(C\),然后从\(C\)坐传送带到\(D\),在从\(D\)\(B\)。同时,我们又知道\(xor\)的性质,\(xor\)同一个数两次等于没有\(xor\)这个数,所以\(A\)\(C\)就可以转化为从\(A\)到根节点再到\(C\)

  所以题目本质上是要找四条从某个点到根的链,使得四条链的异或和最大,并且其中有两条链的端点是固定的。

  既然是\(xor\),那么肯定要用\(trie\)树。
考虑到时间复杂度的要求,又是从根节点到端点,那么我们就想到通过初始化的方式初始化出每一个节点到根节点的异或和,同时弄出深度(之后有用)。

  为了时间复杂度,那么我们肯定不能求解过程中在树上找最大的
那么我们就想到先把所有有可能出现的值放进\(trie\)树里面,之后找的时候就可以实现\(o(n)\)了。也就是\(n^{2}\)的遍历整棵树,把所有的可能的两条链\(xor\)值都放进去(同时这里可以开一个\(flag\)数组,已经放过的就不用再放了),那么查找的时候也只要将端点\(AB\)到根的\(xor\)值用来再\(trie\)树里查找就行了。

 

  然而。。。这题就这么完了吗\(?\)

 

  注意到题目中还有一类操作,显然我们没有考率到所有路径异或上一个值的影响,但是我们不可能一异或之后就重新建树\((TLE警告)\),那么我们想到刚才提过的\(xor\)的性质(只和次数的奇偶性有关),那么自然而然的就联想到和深度的关系。那么自然就是如果深度不同,\(AB\)到根的\(xor\)值就要再异或上一个值\(value\),反之则不用.

 

  但是。。。这样就能\(AC\)了吗\(?\)

 

  这里我们又有了问题,在异或上一个值后,\(trie\)树本身已经改变了,而不是简简单单直接把\(AB\)到根节点的值异或上那个影响就可以的,那我们该怎么去区别这种影响呢\(?\)

  那么我们很自然的\((一点也不)\)想到,只要把会受影响的\(xor\)值和不受影响的\(xor\)值分开来建树就好了。
那么我们在建树之前,先利用\(xor\)的性质,判断出那些\(xor\)值回收影响,并且用二维数组\(exist\)来记录
然后根据\(exist\)数组将这些值分别放进不同的树中
那么我们在查找时,只需要根据深度奇偶性的区别,放到对应的树分别查找就好了,一个用直接到根节点的\(xor\)值,另一个需要再异或一个值进去查找。

  最后附上\(AC\)的代码\(:\)

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int Maxn=(5e5+5)*2;
const int Maxw=(1<<12)+5;
int vis[1<<24]={0},head[Maxn],nxt[Maxn],to[Maxn],val[Maxn],tot=0,depth[Maxn]={0},prexor[Maxn],key=0,n,m;
void dfs(int x,int father)
{
    depth[x]=depth[father]+1;
    for(int e=head[x];e;e=nxt[e])
    {
        int v=to[e];
        if(v==father) continue;
        prexor[v]=prexor[x]^val[e];
        dfs(v,x);
    }
}
void add(int x,int y,int w)
{
    nxt[++tot]=head[x];head[x]=tot;to[tot]=y;val[tot]=w;
    nxt[++tot]=head[y];head[y]=tot;to[tot]=x;val[tot]=w;
}
class Trie
{
public:
    int trie[1<<24][2]={0};
    int tot=0;
    void insert(int val)
    {
        int x=0;
        for(int i=(1<<12);i;i>>=1){
            bool c=val&i;
            if(!trie[x][c]){
                trie[x][c]=++tot;
            }
            x=trie[x][c];
        }
    }
    int query(int val,int x) {
        int ans = 0;
        for (int i = (1 << 12); i; i >>= 1) {
            bool c = val & i;
            if (trie[x][!c]) {
                ans += i;
                x = trie[x][!c];
            } else x = trie[x][c];
        }
        return ans;
    }
};
Trie t[2];
bool flag[2][5005], exist[2][5005];
void build()
{
    for (int i = 1; i <= n; i++)
        flag[depth[i] & 1][prexor[i]] = 1;
    for (int i = 0; i < (1 << 12); i++)
        for (int j = 0; j < (1 << 12); j++)
        {
            exist[0][i ^ j] |= ((flag[0][i] & flag[0][j]) | (flag[1][i] & flag[1][j]));
            exist[1][i ^ j] |= ((flag[0][i] & flag[1][j]) | (flag[1][i] & flag[0][j]));
        }
    for (int i = 0; i < (1 << 12); i++)
    {
        if (exist[0][i])
            t[0].insert(i);
        if (exist[1][i])
            t[1].insert(i);
    }
}
int result(int u,int v)
{
    int k = (depth[u] & 1) ^ (depth[v] & 1),p,q;
    if(k==0) {
        p = t[0].query(prexor[u] ^ prexor[v], 0);
        q = t[1].query(prexor[u] ^ prexor[v] ^ key, 0);
    }
    else{
        p = t[1].query(prexor[u] ^ prexor[v], 0);
        q = t[0].query(prexor[u] ^ prexor[v] ^ key, 0);
    }
    return max(p,q);
}
int main() {
    int x,y,w,opt,v;
    cin>>n>>m;
    for(int i=1;i<n;++i)
    {
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);
    }
    dfs(1,0);
    build();
    while(m-->0)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",result(x,y));
        }
        else
        {
            scanf("%d",&v);
            key=key^v;
        }
    }
    return 0;
}
posted @ 2022-04-15 22:16  源小枔儿  阅读(24)  评论(0编辑  收藏  举报