题目链接:
题解思路:
面对10^5个 名字和10^5条询问,肯定要用到特殊的方法:
1.把所有的询问先存下来,然后再遍历一次整棵树得到所有答案
2.遍历的过程中 查询含当前节点的 所有询问,然后找到询问中的另一个节点;查看另一个节点的状态。
如果另一个节点未访问过,接下来处理;
如果另一个节点正在被访问(子节点未访问完),则答案就是这个节点
如果另一个节点已被访问过,则答案是它的正在被访问的根节点
3. 第二点的状态需要并查集处理
未遍历时所有节点标记为未访问
开始遍历,该节点标记为自己的下标
遍历完后和它的父节点合并
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #define MAXN 100050 using namespace std; map<string,int>id; //保存名字的标号 vector<int>edge[MAXN]; //邻接表 vector<int>query[MAXN]; //保存含某个名字的所有问题的标号 string name[MAXN]; //保存名字 string q1[MAXN],q2[MAXN]; //保存每个问题的左右两个名字 int ans[MAXN]; int fa[MAXN]; int name_s=1; int get_id(string str) { if(!id[str]) { id[str]=name_s; name[name_s++]=str; } return id[str]; } int Find(int x) { return x==fa[x]?x:fa[x]=Find(fa[x]); } void dfs(int loc,int pre) //前序遍历 { fa[loc]=loc; for(int i=0;i<edge[loc].size();i++) dfs(edge[loc][i],loc); for(int i=0;i<query[loc].size();i++) //含这个节点(名字)的所有问题 { int j=query[loc][i]; int tmp=loc==id[q1[j]]? id[q2[j]] : id[q1[j]]; //找当前问题的另一个名字 if(fa[tmp]==-1) //另一个名字没访问过 continue; ans[j]=Find(tmp); } fa[loc]=Find(fa[pre]); //合并到当前节点的根节点 return; } int main() { int n; string a,b; int t1,t2; scanf("%d",&n); for(int i=1;i<=n;i++) { cin>>a>>b; t1=get_id(a); t2=get_id(b); edge[t1].push_back(t2); } scanf("%d",&n); for(int i=1;i<=n;i++) { cin>>q1[i]>>q2[i]; query[id[ q1[i] ] ].push_back(i); query[id[ q2[i] ] ].push_back(i); } memset(fa,-1,sizeof(fa)); fa[0]=0; dfs(1,0); for(int i=1;i<=n;i++) cout<<name[ans[i]]<<endl; return 0; }
时间: 2024-11-03 21:42:13