原题链接: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;
}