E. Milk Visits
https://codeforces.com/group/5yyKg9gx7m/contest/269717/problem/E
题目描述:
农夫约翰的M个朋友(1≤M≤105)常来拜访他。 在与朋友i进行访问期间,农夫约翰将与他的朋友一起沿着从农场Ai到农场Bi的独特道路行走(可能是Ai = Bi)。 此外,他们可以沿行进路线尝试从任何母牛身上获取牛奶。 由于约翰农民的大多数朋友也是农民,因此他们对牛奶有很强的偏好。 他的一些朋友只会喝g牛奶,其余的朋友只会喝h牛奶。 只有农夫约翰的朋友们在参观期间可以喝自己喜欢的牛奶,他们才会高兴。
分析:
先构造出树,可以看出,从x到y的路上一定经过他们的公共祖先。h[i]记录为他从最顶端的根r到i的h牛奶的多少,同理g[i]表示g牛奶。
用倍增求lca,如果只考虑h牛奶,可以看出x到y路上h牛奶的多少为h[x]+h[y]-h[lca]-h[fa[lca]],其中要包含lca的点。g也是同理。
至于要建立树,因为双向图,可以用2个vector分别表示x,y的儿子节点。再dfs遍历树时从人一点出发(下面从点1),每个节点访问一次。也可以遍历完树。
代码:
#include<vector> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> using namespace std; typedef long long ll; const int N=1e5+6; vector<int> son[N]; int dep[N],fa[N][30]; int h[N],g[N]; int n,m; char s[N]; bool vis[N]; void dfs(int prev,int rt) { //遍历树,预处理从rt上移情况 vis[rt]=1; dep[rt]=dep[prev]+1; fa[rt][0]=prev; h[rt]+=h[prev]; g[rt]+=g[prev]; for(int i=1;i<25;i++) fa[rt][i]=fa[fa[rt][i-1]][i-1]; for(int i=0;i<son[rt].size();i++) { if(!vis[son[rt][i]]) dfs(rt,son[rt][i]); } } int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=24;i>=0;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i]; if(x==y) return x; for(int i=24;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int main() { cin>>n>>m; cin>>s; for(int i=0;i<=n;i++) son[i].clear(); memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) { if(s[i-1]=='H') h[i]=1,g[i]=0; else h[i]=0,g[i]=1; } for(int i=1;i<n;i++) { int a,b; scanf("%d%d",&a,&b); son[a].push_back(b); son[b].push_back(a); } dep[0]=-1; dfs(0,1); char ans[m+6]; for(int i=0;i<m;i++) { int x,y; char c; scanf("%d %d %c",&x,&y,&c); int lca=LCA(x,y); if(c=='H') { if(h[x]+h[y]-h[lca]-h[fa[lca][0]]!=0) ans[i]='1'; else ans[i]='0'; } else { if(g[x]+g[y]-g[lca]-g[fa[lca][0]]!=0) ans[i]='1'; else ans[i]='0'; } } ans[m]='\0'; printf("%s\n",ans); return 0; }