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。

Solution

这道题有两种方法:

  • 树链剖分
  • DFS预处理异或前缀和

一.树链剖分
就是一个板子,直接把边权化为点权既可做.
时间复杂度 O(mlogn)

二. DFS处理异或处理前缀和

首先要知道异或的基本性质 :
\[{(x+y)}\bigoplus{y}=x\]

然后这道题,我们将边权转化为点权。
每次需要的是从 LCA(x,y)-x 以及 LCA(x,y)-y ;
然后我们记录根节点到每一个点的异或前缀和.
此时:
\[Ans={Xorsum_{x}}\bigoplus{Xorsum_{y}}\bigoplus{Xorsum_{LCA(x,y)}}\]
然后我们直接 DFS 预处理,然后 O(1)查询即可.
时间复杂度O(n+m)

代码

一.树链剖分

#include<bits/stdc++.h>
using namespace std;
const int maxn=200008;
int n,m;
struct sj{
    int to;
    int next;
    int w;
}a[maxn];
int size,head[maxn];

void add(int x,int y,int z)
{
    a[++size].to=y;
    a[size].w=z;
    a[size].next=head[x];
    head[x]=size;
}
int dep[maxn],fa[maxn];
int top[maxn],son[maxn];
int siz[maxn];

void dfs(int x)
{
    siz[x]=1;
    for(int i=head[x];i;i=a[i].next)
    {
        int tt=a[i].to;
        if(!siz[tt])
        {
            dep[tt]=dep[x]+1;
            fa[tt]=x;
            dfs(tt);
            siz[x]+=siz[tt];
            if(siz[tt]>siz[a[son[x]].to])
            son[x]=i;
        }
    }
}

int id[maxn],num,c[maxn];
void dfs1(int x,int y)
{
    top[x]=y;
    if(son[x])
    {
        int tt=a[son[x]].to;
        c[++num]=a[son[x]].w;
        id[a[son[x]].to]=num;
        dfs1(tt,y);
    }
    for(int i=head[x];i;i=a[i].next)
    {
        int tt=a[i].to;
        if(!top[tt])
        if(i!=son[x])
        {
            c[++num]=a[i].w;
            id[a[i].to]=num;
            dfs1(tt,tt);
        }
    }
}

int sgm[maxn*4];
void build(int node,int l,int r)
{
    if(l==r)
    {sgm[node]=c[l];return;}
    int mid=(l+r)/2;
    build(node*2,l,mid);
    build(node*2+1,mid+1,r);
    sgm[node]=(sgm[node*2]^sgm[node*2+1]);
}

int query(int node,int l,int r,int L,int R)
{
    if(l>R||r<L)return 0;
    if(l>=L&&r<=R)return sgm[node];
    int mid=(l+r)/2;
    return (query(node*2,l,mid,L,R))^(query(node*2+1,mid+1,r,L,R));
}

int check(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        ans^=query(1,1,n,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    ans^=query(1,1,n,id[x]+1,id[y]);
    return ans;
}

int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z); add(y,x,z);
    }
    dfs(1); dfs1(1,1);
    build(1,1,n);
    cin>>m;
    while(m--)
    {
        int x,y; 
        scanf("%d%d",&x,&y);
        cout<<check(x,y)<<endl;
    }
}

二.DFS预处理

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int len,last[1001000],sum[1010000],k,p[35];
struct node
{
    int to,next,w;
}a[1010000];
void add(int a1,int a2,int a3)
{
    len++;
    a[len].to=a2;
    a[len].w=a3;
    a[len].next=last[a1];
    last[a1]=len;
}
void dfs(int x,int father)
{
    for(int i=last[x];i;i=a[i].next)
    {
        int to=a[i].to;
        if(to==father) continue;
        sum[to]=sum[x]^(a[i].w);
        dfs(to,x);
    }
}
int main()
{
    int n,x,y,z,ans=0;
    cin>>n;
    for(int i=1;i<n;i++)
    scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
    dfs(1,0);
    int m;
    cin>>m;
    for(int i=1;i<=m;i++)
    scanf("%d%d",&x,&y),printf("%d\n",sum[x]^sum[y]);
}

猜你喜欢

转载自www.cnblogs.com/Kv-Stalin/p/9240764.html