2017 ACM-ICPC 亚洲区(西安赛区)网络赛 Xor

原题链接:https://nanti.jisuanke.com/t/17120

题目大意:给出一颗n个点的带点权的树,问从a到b的最短路径上第k*p个点的异或和是多少。(n<=5*10^4 , k<=n,q<=5*10^5

先吐槽一下,可能我是中了一种叫做赛后1min过题的诅咒QAQ。

这题的大概思路是,先用树链剖分预处理一下,再在求lca 的时候求异或和。对于k小于200的点,可以有足够的时间和空间来预处理以k为间距的前缀异或和,k大于200的点则用暴力去处理得到异或和。

代码:

#include <bits/stdc++.h>

using namespace std;
inline void read(int &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

inline void read(long long &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}


inline void write(int x){
    static const int maxlen=100;
    static char s[maxlen];
        if (x<0) {   putchar('-'); x=-x;}
    if(!x){ putchar('0'); return; }
    int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
    for(int i=len-1;i>=0;--i) putchar(s[i]);
}

int const MAXN=51000;
int const MAXM=110000;

int n,m;
int pre[ MAXM ] , now[ MAXN ] ,son[ MAXM ],tot;

int pos[ MAXN ],cnt;

int siz[ MAXN ] , deep[ MAXN ];
int top[ MAXN ] , fa[ MAXN ];


void build(int a,int b){
pre[++tot]=now[a];
now[a]=tot;
son[tot]=b;
}


void get_size_deep_fa(int x){
siz[ x ] = 1;
for (int p=now[x];p;p=pre[p])
    if( son[p] != fa[ x ] )
        {
            deep[ son[p] ] = deep[ x ] + 1;
            fa[ son[p] ] = x;
            get_size_deep_fa( son[p]  );
            siz[ x ] += siz[ son[p] ];
        }
}

void get_pos_top(int x,int fa){
bool op=0;
int Max = 0;
int Maxi = 0 ;
++cnt;  pos[ x ] = cnt;
for (int p = now[ x ] ; p ; p=pre[p] )
    if ( son[p] != fa )
        if ( Max < siz[ son[p] ] )
            {
                Max = siz[ son[p] ];
                Maxi = son[p] ;
            }
if ( Maxi )
    {
        top[ Maxi ] = top[ x ];
        get_pos_top( Maxi , x );
    }
for (int p = now[ x ]; p ; p=pre[p] )
    if ( ( son[p]!=fa) && ( son[p]!=Maxi ) )
        {
            top[ son[p] ] = son[p];
            get_pos_top( son[p] , x );
        }
}


int sum[ MAXN ][210];
int a[ MAXN ];

void prepare(){
for (int i=1;i<=200;i++)
    {
        for (int k=0;k<i;k++)
        {
            sum[k][i]=a[k];
            for (int j=k+i;j<=n;j+=i)
                sum[j][i]=( a[j]^sum[j-i][i] );
        }
    }
}

int get_left(int ed,int st,int lim){
int len=ed-st;
int tmp=( len+lim-1 ) /lim*lim;
return ed-tmp;
}

int ans;
void jump(int &y, int aim ,int &ty ,int lim){
if ( lim<=200)
    {
        if (  pos[ y ] - ( lim - ty )%lim >= pos [ aim ] )
            ans = ans^ ( sum[  pos[ y ] - ( lim - ty )%lim ][ lim ] ^ sum[ get_left( pos[ y ] - ( lim - ty )%lim ,  pos[ aim ] -1 ,lim ) ] [ lim ] );
    }
else
    for (int k=pos[ y ]-( lim - ty )%lim;k>=pos[ aim ];k-=lim)
        ans=ans^a[ k ];
ty = ( ty + deep[ y ] - deep[ aim ]+1 ) %lim;
}


int get_lca(int u ,int v,int lim){
    int x = u , y= v;
    ans=0;
    int len=0;
    while ( top[ x ] != top[ y ] )
        {
            if ( deep [ top[ x ] ] > deep [ top[ y ] ])
                swap ( x , y );
            len += deep[ y ] - deep[ top[ y ] ] +1;
            y = fa [ top[ y ] ];
        }
    len += abs( deep[ y ] - deep[ x ] ) ;
    x = u , y =v;
    int tx=0,ty=( lim-len%lim )%lim;
    while ( top[ x ] != top[ y ] )
        {
            if ( deep [ top[ x ] ] > deep [ top[ y ] ])
            {
                swap ( x , y );
                swap ( tx , ty );
            }
            jump( y , top[y] , ty , lim );
            y = fa [ top[ y ] ];
        }
    if ( deep [ x  ] > deep [ y  ])
        {
            swap ( x , y );
            swap ( tx , ty );
        }
    jump ( y , x , ty , lim );
    return ans;
}




int main(){
    while (scanf("%d%d",&n,&m)!=EOF)
        {
            tot=0;
            memset(now,0,sizeof(now));
            memset(siz,0,sizeof(siz));
            for (int i=1;i<n;i++)
                {
                    int a,b;
                    read(a); read(b);
                    build( a , b );
                    build( b , a );
                }
            cnt=0;
            deep[1]=0;
            fa[1]=0;
            get_size_deep_fa( 1 );
            top[1]=1;
            get_pos_top( 1 , 0 );
            for (int i=1;i<=n;i++)
                read(a[ pos [i] ]);
            prepare();
            for (int i=1;i<=m;i++)
                {
                    int a,b,c;
                    read( a); read(b); read(c);
                    write( get_lca(a,b,c) );
                    puts("");
                }
        }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值