• 让我们异或吧


    洛谷原创的这道题说实话真的不错。。。

    题目大意:

    求一个树上两个点路径所有边的异或和。

    树上求和问题很显然会想到LCA吧,我们假定两个点是u和v,他们的LCA为lca(a,b)

    我们维护一个dis数组,dis[i]代表从根节点到i的路径边权的异或和。

    我们要求的是两点之间的异或和,我们想从做过的题型中找思路:如果只是简单的求和的话,我们只要用a,b的dis减去两倍lca的dis即可。

    但是现在是异或,我们还是从这上去想:我们利用lca来作为中间量。

    我们会发现一个数连续异或两次就等于没有异或,并且x^0=x(显然的吧。。。)

    所以我们求两点路径间的异或和就可以转化成求两点到根节点的dis的异或和。

    所以本题不用lca,只需要遍历一遍树,求出每个点的dis就可以了。

    答案就是dis[a]^dis[b].

    最后,附上本题代码:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define maxn 100000
     4 using namespace std;
     5 
     6 int n,head[maxn+5],cnt,m;
     7 int dep[maxn+5],dis[maxn+5];
     8 struct EDGE
     9 {
    10     int to,nxt,v;
    11 };
    12 EDGE edge[maxn*2+5];
    13 
    14 void add(int x,int y,int z)
    15 {
    16     edge[++cnt].to=y;
    17     edge[cnt].v=z;
    18     edge[cnt].nxt=head[x];
    19     head[x]=cnt;
    20 }
    21 void pre_fir(int fa,int u)
    22 {
    23     for(int i=head[u]; i; i=edge[i].nxt)
    24     {
    25         if(edge[i].to==fa) continue;
    26         dis[edge[i].to]=dis[u]^edge[i].v;
    27         pre_fir(u,edge[i].to);
    28     }
    29 }
    30 int main()
    31 {
    32     scanf("%d",&n);
    33     for(int i=1; i<=n-1; i++)
    34     {
    35         int x,y,z;
    36         scanf("%d%d%d",&x,&y,&z);
    37         add(x,y,z);
    38         add(y,x,z);
    39     }
    40     scanf("%d",&m);
    41     pre_fir(0,1);
    42     int a,b;
    43     for(int i=1; i<=m; i++)
    44     {
    45         scanf("%d%d",&a,&b);
    46         printf("%d
    ",dis[a]^dis[b]);
    47     }
    48     return 0;
    49 }
  • 相关阅读:
    AD域新建用户
    Windows程序设计(1)
    C++ 进阶 模板和STL
    C++ 基于多态的职工管理系统
    C++核心编程
    C++入门-控制台版的通讯录管理系统
    第一章、熟悉工作环境和相关工具
    Spring整合JDBC temple
    SpringMVC整合mybaitis
    实验12 添加0号中断处理程序
  • 原文地址:https://www.cnblogs.com/yufenglin/p/10581137.html
Copyright © 2020-2023  润新知