2017 ACM-ICPC 亚洲区(西安赛区)网络赛 G. Xor [LCA+分块]

5 篇文章 0 订阅
4 篇文章 0 订阅

题意:给你一棵树,树上每个点有一个值,q个询问,每个询问求u到v的路径上,下标为0,k,2*k···的点的异或和为多少。

题解:分块的思想,我们对于每个点,记录他往上跳len步到root的异或和,对于询问假如k大于len,我们一步一步跳,假如小于len,我们直接用记录的数据计算出答案。

这题细节很多= =。

AC代码:

#include<stdio.h> 
#include<vector>  
#include<math.h>  
#include<string.h>  
#define N 50005  
using namespace std;  
vector<int>vt[N];  
int deep[N],p[N][20],n,m,len;  
int a[N],sum[N][255];  
void dfs(int u,int fa)   
{   
    int now=u,step=0;  
    deep[u]=deep[fa]+1;  
    p[u][0]=fa;  
    while(step<255)  
    {  
        sum[u][step]=a[u]^sum[now][step];  
        now=p[now][0];  
        step++;  
    }    
    for(int i=0;i<vt[u].size();i++) {     
        int to=vt[u][i];  
        if(to==fa)continue;  
        dfs(to,u);  
    }     
}    
void init() {     
    int i,j; //p[i][j]表示i结点的第2^j祖先    
    for(j=1;(1<<j)<=n;j++)     
        for(i=1;i<=n;i++)     
            if(p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];    
          //i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先     
}    
int lca(int a,int b) {//最近公共祖先     
    int i,j;     
    if(deep[a]<deep[b])swap(a,b);     
    for(i=0;(1<<i)<=deep[a];i++); i--;     
    for(j=i;j>=0;j--) //使a,b两点的深度相同     
        if(deep[a]-(1<<j)>=deep[b]) a=p[a][j];     
    if(a==b)return a;     
    for(j=i;j>=0;j--) {     
        if(p[a][j]!=-1&&p[a][j]!=p[b][j]) {     
            a=p[a][j]; b=p[b][j];     
        }    
    }     
    return p[a][0];     
}    
int getk(int now,int x)  
{  
    for(int i=19;i>=0;--i)  
        if(x>=(1<<i))  
        {  
            now=p[now][i];  
            x-=(1<<i);  
            if(now==0)break;  
        }  
    return now;  
}  
int main()  
{  
    while(~scanf("%d%d",&n,&m))  
    {  
        memset(sum,0,sizeof(sum));  
        for(int i=0;i<50005;i++)vt[i].clear();  
        len=(int)sqrt(n);deep[1]=1;  
        for(int i=0;i<n-1;i++)  
        {  
            int u,v;  
            scanf("%d%d",&u,&v);  
            vt[u].push_back(v);  
            vt[v].push_back(u);  
        }  
        for(int i=1;i<=n;i++)  
            scanf("%d",&a[i]);  
        dfs(1,0);  
        init();  
        while(m--)  
        {  
            int u,v,k;  
            scanf("%d%d%d",&u,&v,&k);  
            int lc=lca(u,v);  
            int numu=deep[u]-deep[lc];  
            int numv=deep[v]-deep[lc];  
            int ans;  
            if(k<=len)  
            {  
                ans=sum[u][k];  
                int s1=(numu)%k;  
                numu=k-s1;  
                int now=lc;  
                now=getk(now,numu);  
                ans^=sum[now][k];  
                int need=numv-numu;  
                if(need>=0&&v!=lc)  
                {  
                    need%=k;  
                    v=getk(v,need);  
                    ans^=sum[v][k];  
                    s1=(numv-need)%k;  
                    numv=(k-s1)%k;  
                    now=lc;  
                    now=getk(now,numv);  
                    ans^=sum[now][k];  
                }  
            }  
            else   
            {  
                int now=u;ans=0;  
                while(deep[now]>=deep[lc])  
                {  
                    ans^=a[now];  
                    now=getk(now,k);  
                }  
                int s1=(numu)%k;  
                numu=k-s1;  
                int need=numv-numu;  
                if(need>=0&&v!=lc)  
                {  
                    now=v;  
                    need%=k;  
                    now=getk(now,need);  
                    while(deep[now]>deep[lc])  
                    {  
                        ans^=a[now];  
                        now=getk(now,k);  
                    }  
                }  
            }  
            printf("%d\n",ans);  
        }  
    }  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值