Closest Common Ancestors
Time Limit: 2000MS | Memory Limit: 10000K | |
Total Submissions: 18141 | Accepted: 5807 |
Description
Write a program that takes as input a rooted tree and a list of pairs of vertices. For each pair (u,v) the program determines the closest common ancestor of u and v in the tree. The closest common ancestor of two nodes u and v is the node w that is an ancestor of both u and v and has the greatest depth in the tree. A node can be its own ancestor (for example in Figure 1 the ancestors of node 2 are 2 and 5)
Input
The data set, which is read from a the std input, starts with the tree description, in the form:
nr_of_vertices
vertex:(nr_of_successors) successor1 successor2 ... successorn
...
where vertices are represented as integers from 1 to n ( n <= 900 ). The tree description is followed by a list of pairs of vertices, in the form:
nr_of_pairs
(u v) (x y) ...
The input file contents several data sets (at least one).
Note that white-spaces (tabs, spaces and line breaks) can be used freely in the input.
nr_of_vertices
vertex:(nr_of_successors) successor1 successor2 ... successorn
...
where vertices are represented as integers from 1 to n ( n <= 900 ). The tree description is followed by a list of pairs of vertices, in the form:
nr_of_pairs
(u v) (x y) ...
The input file contents several data sets (at least one).
Note that white-spaces (tabs, spaces and line breaks) can be used freely in the input.
Output
For each common ancestor the program prints the ancestor and the number of pair for which it is an ancestor. The results are printed on the standard output on separate lines, in to the ascending order of the vertices, in the format: ancestor:times
For example, for the following tree:
For example, for the following tree:
![](https://i-blog.csdnimg.cn/blog_migrate/f3543bc96c2a6975718af0ddff6b73a7.jpeg)
Sample Input
5 5:(3) 1 4 2 1:(0) 4:(0) 2:(1) 3 3:(0) 6 (1 5) (1 4) (4 2) (2 3) (1 3) (4 3)
Sample Output
2:1 5:5
题意:有向图,输入一个点,括号里输入有几个儿子,后面输入儿子,输入完毕,输入询问查询公共祖先,最后输出每个点的
作为公共祖先的次数,次数为0的不输出
开始以为是简单的LCA问题,然后RE,然后看了下1470下面的discuss和其他的题解,才发现有好几个坑。
1.询问有多次,有说询问要开50W的
2.重复询问相同的 数据
我用vector存的询问,不知道为什么会re,然后看别人用的邻接矩阵,也用邻接矩阵,A了,虽然还是不知道为什么vector会re
学到一个点是vector开出内存后进行clear操作不会释放内存(不要问我怎么知道的,mle血淋淋的教训),至于好的解决办法我去找了下
传送门,大概意思就是开一个临时vector变量,进行swap就行了。
我的更详解的Tarjan理解:
传送门
CODE:
#include"stdio.h"
#include"iostream"
#include"algorithm"
#include"string.h"
#include"vector"
using namespace std;
const int maxn = 1000+10;
struct node ///邻接表
{
int to;
int next;
}e[maxn];
int n,m;
int top; ///邻接表边的条数
int ans[maxn]; ///次数
int head[maxn]; ///邻接表头结点
int fa[maxn]; ///并查集的父亲结点
int in[maxn]; ///定点入度,用来寻找根节点
int vis[maxn]; ///Tarjan里面进行标记
int flag[maxn][maxn]; ///一个询问可能出现多次,正向或者反向的询问都只能加一次,所以需要标记
int query[maxn][maxn];///询问次数
void INIT() ///初始化
{
top = 0;
for(int i = 0;i < maxn;i++)
{
ans[i] = vis[i] = in[i] = 0;
head[i] = -1;
fa[i] = i;
}
memset(query,0,sizeof query);
memset(flag,0,sizeof flag);
}
void add(int u,int v) ///邻接表加边
{
e[top].to = v;
e[top].next = head[u];
head[u] = top++;
}
int Find(int x)
{
if(x != fa[x])
fa[x] = Find(fa[x]);
return fa[x];
}
void Union(int x,int y) ///并查集联合
{
int fx = Find(x);
int fy = Find(y);
fa[fy] = fx;
}
void Tarjan(int u)
{
vis[u] = 1;
for(int i = head[u];i != -1;i = e[i].next)
{
int t = e[i].to;
Tarjan(t);
Union(u,t);
}
for(int i = 1;i <= n;i++)
{
if(vis[i] && query[u][i] && !flag[u][i])
ans[Find(i)] += query[u][i],flag[u][i] = flag[i][u] = 1; ///双向加标记,一种询问只能加一次
}
}
int main(void)
{
while(scanf("%d",&n) != EOF)
{
INIT();
for(int i = 1;i <= n;i++)
{
int h,sons;
scanf("%d:(%d)",&h,&sons);
for(int j = 1;j <= sons;j++)
{
int son;
scanf("%d",&son);
add(h,son);
in[son]++;
}
}
scanf("%d",&m);
for(int i = 1;i <= m;i++)
{
int a,b;
scanf(" (%d %d)",&a,&b);
query[a][b]++;
query[b][a]++; ///双向加
}
for(int i = 1;i <= n;i++)
if(in[i] == 0)
Tarjan(i);
for(int i = 1;i <= n;i++)
{
if(ans[i])
{
printf("%d:%d\n",i,ans[i]);
}
}
}
return 0;
}