2420 让我们异或吧

2420 让我们异或吧

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int n;
int head[200000];
int depth[200000];
int f[200000][18][2];//f[i][j][0]表示从i开始往上走2^j个节点,(*重要)f[i][j][1]表示从i开始往上走2^j个节点包含的2^j条边的异或和
int cnt=0;
struct Edge//链式前向星
{
    int z;
    int nexty;
    int qz;
}edge[400000];//记得开大一点,因为是存有向边
inline int dy()//读入优化
{
    int num=0;
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')num=num*10+c-'0',c=getchar();
    return num;
}
inline void add(int f,int z,int qz)//建边函数
{
    cnt++;
    edge[cnt].z=z;
    edge[cnt].qz=qz;
    edge[cnt].nexty=head[f];
    head[f]=cnt;
}
inline void build(int root)//建树函数
{
    int will,wqz;
    for(int now=head[root];now!=-1;now=edge[now].nexty)
    {
        will=edge[now].z;
        wqz=edge[now].qz;
        if(!depth[will])
        {
            depth[will]=depth[root]+1;
            f[will][0][0]=root;
            f[will][0][1]=wqz;
            _build(will);
        }
    }
}
inline int yh(int a,int b)//求异或和,用位运算,方法如前解析所述
{
    int yhh=0;
    int sf=0;
    int aw,bw;
    while(a|b)
    {
        aw=a&1;
        bw=b&1;
        yhh+=((aw|bw)&((!aw)|(!bw)))<<(sf++);
        a>>=1,b>>=1;
    }
    return yhh;
}
inline void work()//设置f[i][j][k]的值,用到一点DP思想
{
    for(int i=1;i<=17;i++)
    {
        for(int j=1;j<=n;j++)//注意这两个循环的顺序,若反过来,则会先访问未被计算过的f[i][j][k]值
        {
            f[j][i][0]=f[f[j][i-1][0]][i-1][0];
            f[j][i][1]=yh(f[j][i-1][1],f[f[j][i-1][0]][i-1][1]);//求异或和
        }
    }
}
inline int lca(int a,int b)//求异或和
{
    if(depth[a]<depth[b])swap(a,b);
    int yhh=0;//存储异或和,因为a与0的异或和必为a,所以可将初始值设为0
    for(int i=17;i>=0;i--)
    {
        if(depth[f[a][i][0]]>=depth[b])
        {
            yhh=yh(yhh,f[a][i][1]);
            a=f[a][i][0];
        }
    }
    if(a==b)return yhh;
    for(int i=17;i>=0;i--)
    {
        if(f[a][i][0]!=f[b][i][0])
        {
            yhh=yh(yh(f[a][i][1],f[b][i][1]),yhh);
            a=f[a][i][0],b=f[b][i][0];
        }
    }
    return yh(yh(f[a][0][1],f[b][0][1]),yhh);
}
int main()
{
    depth[1]=1;
    memset(head,-1,sizeof(head));
    n=dy();
    int a,b,qz;
    for(int i=0;i<n-1;i++)
    {
        a=dy();b=dy();qz=dy();
        add(a,b,qz);
        add(b,a,qz);//加边(双向)
    }
    build(1);//建树
    work();//建f
    int m;
    m=dy();
    for(int i=0;i<m;i++)
    {
        a=dy();b=dy();
        printf("%d\n",lca(a,b));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值