• 洛谷——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;
    }
  • 相关阅读:
    用DecimalFormat格式化十进制数字的实际应用
    ETL测试小结
    浅谈DB2的四个隔离级别
    POI处理Excel中各种日期格式问题
    如何将Object类型转换成String类型
    ArrayList和LinkedList
    DB2 编目并访问远程数据库
    EXCEL解析之终极方法WorkbookFactory
    DB2中coalesce函数的应用
    【Linux学习】Linux用户管理2—用户配置文件
  • 原文地址:https://www.cnblogs.com/song-/p/9698294.html
Copyright © 2020-2023  润新知