洛谷——P2420 让我们异或吧

P2420 让我们异或吧

题目描述

异或是一种神奇的运算,大部分人把它总结成不进位加法.

在生活中…xor运算也很常见。比如,对于一个问题的回答,是为1,否为0.那么:

(A是否是男生 )xor( B是否是男生)=A和B是否能够成为情侣

好了,现在我们来制造和处理一些复杂的情况。比如我们将给出一颗树,它很高兴自己有N个结点。树的每条边上有一个权值。我们要进行M次询问,对于每次询问,我们想知道某两点之间的路径上所有边权的异或值。

维护父节点的同时维护抑或值

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>

#define N 1010100
using namespace std;

int n,m,head[N],tot;
struct nodE {
    int to,next,w;
} e[N];

void add(int u,int v,int w) {
    e[++tot].to=v,e[tot].next=head[u],head[u]=tot,e[tot].w=w;
}

int p[N][26],f[N][26],dep[N];
void dfs(int u,int fa,int w) {
    dep[u]=dep[fa]+1,f[u][0]=fa,p[u][0]=w;
    for(int i=1; (1<<i)<=dep[u]; i++)
        f[u][i]=f[f[u][i-1]][i-1],p[u][i]=p[u][i-1]^p[f[u][i-1]][i-1];
    for(int i=head[u]; i; i=e[i].next) {
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,u,e[i].w);
    }
}

int lca(int u,int v) {
    int an=0;
    if(dep[u]>=dep[v]) swap(u,v);
    for(int i=20; i>=0; i--) {
        if(dep[u]<=dep[v]-(1<<i)) {
            if(!an) an=p[v][i];
            else an^=p[v][i];
            v=f[v][i];
        }
    }
    if(u==v) return an;
    for(int i=20; i>=0; i--) {
        if(f[u][i]!=f[v][i]) {
            if(!an) an=p[v][i]^p[u][i];
            else an^=p[v][i],an^=p[u][i];
            u=f[u][i],v=f[v][i];
        }
    }
    if(v==1) return an^p[u][0];
    else if(u==1) return an^p[v][0];
    return an^p[u][0]^p[v][0];
}

int main() {
    scanf("%d",&n);
    for(int u,v,w,i=1; i<n; i++) {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    dfs(1,0,0);
    scanf("%d",&m);
    for(int u,v,i=1; i<=m; i++) {
        scanf("%d%d",&u,&v);
        printf("%d
",lca(u,v));
    }

    return 0;
}

普及一下有关抑或的知识:

抑或: $0^0=0,1^1=0,1^0=1,0^1=1$

观察发现只有二进制位相同时,那一位的抑或值才为真

抑或的几个常用性质:

1.交换律:$a^b=b^a$ 显然

2.结合律:$a^b^c=a^(b^c)$ 显然

3.抑或自己 $a^a=0$ 显然

4.抑或0 $a^0=a$ 显然

设$dis[u]$为节点1到$u$所经过路径上的抑或值

那么本题要求就是$(dis[u]^dis[lca(u,v)])^(dis[v]^dis[lca(u,v)])$

因为抑或自己为0,而抑或0为自己,那么1到点u再抑或1到点$lca(u,v)$,就相当于点u抑或了$lca(u,v)$

毫无疑问,抑或满足结合律$(dis[u]^dis[v])^(dis[lca(u,v)]^dis[lca(u,v)])$

由性质3,4得出$ans=dis[u]^dis[v]$

极其简短的代码以及神奇的性质:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>

#define N 1010100
using namespace std;

int n,m,head[N],tot;
struct nodE {
    int to,next,w;
} e[N];

void add(int u,int v,int w) {
    e[++tot].to=v,e[tot].next=head[u],head[u]=tot,e[tot].w=w;
}

int dis[N];
void dfs(int u,int fa,int Xor){
    dis[u]=Xor;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v!=fa) dfs(v,u,Xor^e[i].w);
    }
}
int main() {
    scanf("%d",&n);
    for(int u,v,w,i=1; i<n; i++) {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    dfs(1,1,1);
    scanf("%d",&m);
    for(int u,v,i=1; i<=m; i++) {
        scanf("%d%d",&u,&v);
        printf("%d
",dis[u]^dis[v]);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/song-/p/9698294.html