• 【洛谷P2420】让我们异或吧


    题目描述

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

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

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

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

    输入输出格式

    输入格式:

    输入文件第一行包含一个整数N,表示这颗开心的树拥有的结点数,以下有N-1行,描述这些边,每行有3个数,u,v,w,表示u和v之间有一条权值为w的边。接下来一行有一个整数M,表示询问数。之后的M行,每行两个数u,v,表示询问这两个点之间的路径上的权值异或值。

    输出格式:

    输出M行,每行一个整数,表示异或值

    输入输出样例

    输入样例#1

    5

    1 4 9644

    2 5 15004

    3 1 14635

    5 3 9684

    3

    2 4

    5 4

    1 1

    输出样例#1

    975

    14675

    0

    说明

    对于40%的数据,有1 ≤ N,M ≤ 3000;

    对于100%的数据,有1 ≤ N ,M≤ 100000。

    算法:

    LCA+数学

     

    分析:

    这道题乍眼一看感觉是带权值的最近公共祖先问题,但粗略计算后发现会超时,进一步分析,得到一些有关异或的规律,然后这就是一道暴力的题目。

     

    引用一些大佬的资料:

    根据题目中对“异或”的定义,我们可以得出异或的真值表,这里我们用a,b代表异或的两个元素,a^b代表a按位异或的值。

        a    b    a^b
        0    0    0
        0    1    1
        1    0    1
        1    1    0

    我们发现,如果a==b,那么a^b就是0,否则式子的值就是1。

    通过真值表,我们可以发现并证明异或的几个性质。

    1.a^b==b^a

    异或具有交换律

        a    b    a^b    b^a
        0    0    0        0
        0    1    1        1
        1    0    1        1
        1    1    0        0

    2.a^b^c==a^(b^c)

    异或具有结合律

        a    b    c    a^b^c    a^(b^c)
        0    0    0    0        0
        0    0    1    1        1
        0    1    0    1        1
        0    1    1    0        0
        1    0    0    1        1
        1    0    1    0        0
        1    1    0    0        0
        1    1    1    1        1

    3.a^a==0

    异或自己是0

        a    a^a
        0    0
        1    0

    4.a^0=a

    异或0还是0

        a    a^0
        0    0
        1    1

    由以上四点性质,我们可以推出:

    a^b^b = a^(b^b)

         =    a^0
        =    a

    所以推出如下定理:

    异或的逆运算是它本身!!!

    讲一下对于本题的具体思路:对于这棵树,随意指定一个根节点,以此点来建树,维护每个点到根节点的距离,两个点u到v的树上路径一定为

    u->lca(u,v)->v

    第一个过程一定是一直向父亲节点前进,第二个过程一定是一直向儿子节点前进。

    (lca(u,v)的定义为u,v的树上最近公共祖先)

    那么最后查询的答案一定为

    length[u]^length[v] anc^ length[lca(u,v)] anc^ length[lca(u,v)]

    其中anc^为^的逆运算,由于之前已经推出anc^等价于^,而两次亦或的结果等价于原结果,所以最后的答案为:

    length[u]^length[v]

    此处不用考虑特判u或v是根节点的情况。

    建树的时间复杂度为O(N),查询只需要O(1)的时间,最后程序的时间复杂度为O(N+M),空间复杂度为O(N)。

     

     

    上代码:

     

     1 #include<cstdio>
     2 #define maxn 100010
     3 using namespace std;
     4 
     5 int n,m,tot,head[maxn],dis[maxn];
     6 struct node
     7 {
     8     int nxt,to,val;
     9 }edge[maxn<<1];
    10 bool vis[maxn];
    11 
    12 int read()
    13 {
    14     int x=0,f=1;
    15     char c=getchar();
    16     while (c<48||c>57)
    17         f=c=='-'?-1:1,c=getchar();
    18     while (c>=48&&c<=57)
    19         x=(x<<1)+(x<<3)+(c^48),c=getchar();
    20     return x*f;
    21 }
    22 
    23 void add(int c,int b,int a)
    24 {
    25     edge[++tot]=(node){head[a],b,c};
    26     head[a]=tot;
    27     edge[++tot]=(node){head[b],a,c};
    28     head[b]=tot;
    29 }
    30 
    31 int dfs(int u,int Xor)
    32 {
    33     dis[u]=Xor;
    34     vis[u]=1;
    35     for (int i=head[u];i;i=edge[i].nxt)
    36         if (!vis[edge[i].to])
    37             dfs(edge[i].to,Xor^edge[i].val);
    38 }
    39 
    40 int main()
    41 {
    42     int i,j,k,u,v;
    43     n=read();
    44     for (i=1;i<=n-1;i++)
    45         add(read(),read(),read());
    46     m=read();
    47     dfs(1,0);
    48     while (m--)
    49         printf("%d
    ",dis[read()]^dis[read()]);
    50     return 0;
    51 }
  • 相关阅读:
    protobuf 中的嵌套消息的使用
    Mysql++详解
    MYSQL++之Connect类型
    c/c++中宏定义##连接符 和#符的使用
    c指针
    linux学习历程
    linux sar 命令详解
    Linux下多线程查看工具(pstree、ps、pstack)
    知识杂项
    python 使用xrld
  • 原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/8999296.html
Copyright © 2020-2023  润新知