题解:[USACO19DEC] Milk Visits S
题意重述
给定一棵无根树,每次给出 \(a_i,b_i,c_i\),求 \(a_i\sim b_i\) 的路径上是否存在一个点的权值为 \(c_i\)。
题意分析
首先这样一眼树链剖分,将一对多的非线性信息转化为线性信息后处理即可。
但是,本题还存在更简单的算法:倍增。
可以参考倍增 LCA,在从 \(u,v\) 跳到 \(\operatorname{lca}(u,v)\) 的过程中判断是否存在 \(c_i\) 即可。
具体而言,就是设 \(G[x][i],H[x][i]\) 表示 \(x\) 至 \(x\) 的 \(2^i\) 级祖先的路径上是否存在 G、H。
其实也可以参考[NOIP2013 提高组]货车运输。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const int N=1e5,M=1e5;
struct edge{
int v,r;
}a[2*(N-1)+1];
int n,m,h[N+1],d[N+1],lg[N+1],f[N+1][__lg(N+1)+1];
bool vg[N+1],vh[N+1],G[N+1][__lg(N+1)+1],H[N+1][__lg(N+1)+1];
void create(int u,int v){
static int top;
a[++top]={v,h[u]};
h[u]=top;
}
void dfs(int x,int fx){
f[x][0]=fx;
G[x][0]=vg[x]||vg[fx];
H[x][0]=vh[x]||vh[fx];
d[x]=d[fx]+1;
for(int i=h[x];i>0;i=a[i].r){
if(a[i].v==fx)continue;
dfs(a[i].v,x);
}
}
void pre(){
for(int i=1;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)lg[i]--;
dfs(1,0);
for(int i=1;i<=lg[n];i++){
for(int x=1;x<=n;x++){
f[x][i]=f[f[x][i-1]][i-1];
G[x][i]=G[x][i-1]||G[f[x][i-1]][i-1];
H[x][i]=H[x][i-1]||H[f[x][i-1]][i-1];
}
}
}
bool query(int u,int v,char ch){
if(d[u]<d[v])swap(u,v);
for(int i=lg[d[u]-d[v]];i>=0;i--){
if(d[f[u][i]]>=d[v]){
switch(ch){
case 'G':
if(G[u][i])return true;
break;
case 'H':
if(H[u][i])return true;
break;
}u=f[u][i];
}
}if(u==v){
switch(ch){
case 'G':
if(vg[u]||vg[v])return true;
break;
case 'H':
if(vh[u]||vh[v])return true;
break;
}return false;
}
for(int i=lg[d[u]];i>=0;i--){
if(f[u][i]!=f[v][i]){
switch(ch){
case 'G':
if(G[u][i]||G[v][i])return true;
break;
case 'H':
if(H[u][i]||H[v][i])return true;
break;
}u=f[u][i],v=f[v][i];
}
}switch(ch){
case 'G':
if(G[u][0]||G[v][0])return true;
break;
case 'H':
if(H[u][0]||H[v][0])return true;
break;
}return false;
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
char ch;
cin>>ch;
switch(ch){
case 'G':vg[i]=true;break;
case 'H':vh[i]=true;break;
}
}for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
create(u,v);create(v,u);
}pre();
while(m--){
int x,y;
scanf("%d %d",&x,&y);
char ch;
cin>>ch;
printf("%d",query(x,y,ch));
}
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号