2017 ACM-ICPC 亞洲區(西安賽區)網路賽 Xor
阿新 • • 發佈:2019-01-03
題目大意:給出一顆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; }