problem
题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入格式:
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:
输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入样例#1:
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出样例#1:
4
4
1
4
4
思路
数据量可以到500000点 500000询问 提交果然卡常了
1.读入可以用getchar优化(次要)
2.vector较链表在数据量大的时候是慢的(希望有大神可以系统的说明下这个vector的复杂度分析),可以用链式邻接表或者链式前向星模拟邻接表
代码示例(链式邻接表)
#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;//节点数
struct Edge{
int from,to;
}edges[2*maxn];
const int maxlog=30;
int grand[maxn][maxlog];
//int gdis[maxn][maxlog];
//int gmax[maxn][maxlog];
int depth[maxn];
int n;//结点数
int s;//倍增最大步数
int root;//根节点
int cnt;//边集数
struct EdgeNode{
int adjvex;//顶点编号
EdgeNode *next;
};
struct AdList{
int flag;
EdgeNode *firstarc;
}G[maxn];
void addEdge(int u,int v){
//edges[cnt].from=v;
//edges[cnt].to=head[u];
//head[u]=cnt++;
edges[cnt].from=u;
edges[cnt++].to=v;
//cout<<"添加"<<edges[cnt-1].from<<" "<<edges[cnt-1].to<<endl;
EdgeNode *e;
e=(EdgeNode *)malloc(sizeof(EdgeNode));
e->adjvex=cnt-1;
if(G[u].flag==0) G[u].firstarc=NULL,G[u].flag++;
e->next=G[u].firstarc;
G[u].firstarc=e;
//cout<<"目前与节点"<<u<<"连接的一个节点在边集数组的位置"
//<<G[u].firstarc->adjvex<<endl;
}
void init()
{
cnt=0;
memset(grand,0,sizeof(grand));
//memset(head,-1,sizeof(head));
memset(depth,0,sizeof(depth));
//memset(gdis,0,sizeof(gdis));
//memset(gmax,0,sizeof(gmax));
}
void dfs(int x)//预处理
{
for(int i=1;i<=s;++i){
grand[x][i]=grand[grand[x][i-1]][i-1];
//gdis[x][i]=gdis[x][i-1]+gdis[grand[x][i-1]][i-1];
//gmax[x][i]=max(gmax[x][i-1],gmax[grand[x][i-1]][i-1]);
//if(!grand[x][i]) break;
}
for(int i=0;G[x].firstarc!=NULL;++i){
//cout<<edges[G[x].firstarc->adjvex].to<<"TEST"<<endl;
int tt=edges[G[x].firstarc->adjvex].to;
G[x].firstarc=G[x].firstarc->next;
if(tt!=grand[x][0]){
depth[tt]=depth[x]+1;
grand[tt][0]=x;
//gdis[e.to][0]=e.dist;
//gmax[e.to][0]=e.dist;
dfs(tt);
}
}
}
int lca(int a,int b)//最大值,路径权值和
{
if(depth[a]>depth[b]) swap(a,b);
//ans=0;//路径权值和
//maxx=gmax[b][0];
//for(int i=s;i>=0;i--)
// if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
// b=grand[b][i];
int dre=depth[b]-depth[a];
for(int i=s;i>=0;--i){
if(dre&(1<<i)) b=grand[b][i];
}
if(a==b) return a;
for(int i=s;i>=0;i--)
if(grand[a][i]!=grand[b][i]){
//ans+=gdis[a][i],ans+=gdis[b][i];
//maxx=max(maxx,gmax[a][i]),maxx=max(maxx,gmax[b][i]);
a=grand[a][i],b=grand[b][i];
}
//ans+=gdis[a][0];
//ans+=gdis[b][0];
//maxx=max(maxx,gmax[a][0]);
//maxx=max(maxx,gmax[b][0]);
return grand[a][0];
}
int read()
{
char ch='*';
while(!isdigit(ch=getchar()));//不是数字读掉
int num=ch-'0';
while(isdigit(ch=getchar())) num=num*10+ch-'0';
return num;
}
int main()
{
init();
int query,u,v,w;
scanf("%d %d %d",&n,&query,&root);
s=floor(log(n+0.0)/log(2.0))+1;
for(int i=1;i<=n-1;++i){
u=read();
v=read();
addEdge(u,v);
addEdge(v,u);
}
dfs(root);//以root为根结点建树
for(int i=1;i<=query;++i){
u=read();
v=read();
printf("%d\n",lca(u,v));
}
return 0;
}
代码示例(链式向前星)
#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;//节点数
struct Edge{
int from,to;
}edges[2*maxn];
const int maxlog=30;
int grand[maxn][maxlog];
//int gdis[maxn][maxlog];
//int gmax[maxn][maxlog];
int head[2*maxn];
int depth[maxn];
int n;//结点数
int s;//倍增最大步数
int root;//根节点
int cnt;//边集数
void addEdge(int u,int v){
edges[cnt].from=v;
edges[cnt].to=head[u];
head[u]=cnt++;
}
void init()
{
cnt=0;
memset(grand,0,sizeof(grand));
memset(head,-1,sizeof(head));
memset(depth,0,sizeof(depth));
//memset(gdis,0,sizeof(gdis));
//memset(gmax,0,sizeof(gmax));
}
void dfs(int x)//预处理
{
for(int i=1;i<=s;++i){
grand[x][i]=grand[grand[x][i-1]][i-1];
//gdis[x][i]=gdis[x][i-1]+gdis[grand[x][i-1]][i-1];
//gmax[x][i]=max(gmax[x][i-1],gmax[grand[x][i-1]][i-1]);
//if(!grand[x][i]) break;
}
for(int i=head[x];i!=-1;i=edges[i].to){
int tt=edges[i].from;
if(tt!=grand[x][0]){
depth[tt]=depth[x]+1;
grand[tt][0]=x;
//gdis[e.to][0]=e.dist;
//gmax[e.to][0]=e.dist;
dfs(tt);
}
}
}
int lca(int a,int b)//最大值,路径权值和
{
if(depth[a]>depth[b]) swap(a,b);
//ans=0;//路径权值和
//maxx=gmax[a][0];
//for(int i=s;i>=0;i--)
// if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
// b=grand[b][i];
int dre=depth[b]-depth[a];
for(int i=s;i>=0;--i){
if(dre&(1<<i)) b=grand[b][i];
}
if(a==b) return a;
for(int i=s;i>=0;i--)
if(grand[a][i]!=grand[b][i]){
//ans+=gdis[a][i],ans+=gdis[b][i];
//maxx=max(maxx,gmax[a][i]),maxx=max(maxx,gmax[b][i]);
a=grand[a][i],b=grand[b][i];
}
//ans+=gdis[a][0];
//ans+=gdis[b][0];
//maxx=max(maxx,gmax[a][0]);
//maxx=max(maxx,gmax[b][0]);
return grand[a][0];
}
int read()
{
char ch='*';
while(!isdigit(ch=getchar()));//不是数字读掉
int num=ch-'0';
while(isdigit(ch=getchar())) num=num*10+ch-'0';
return num;
}
int main()
{
init();
int query,u,v,w;
scanf("%d %d %d",&n,&query,&root);
s=floor(log(n+0.0)/log(2.0))+1;
for(int i=1;i<=n-1;++i){
u=read();
v=read();
addEdge(u,v);
addEdge(v,u);
}
dfs(root);//以root为根结点建树
for(int i=1;i<=query;++i){
u=read();
v=read();
printf("%d\n",lca(u,v));
}
return 0;
}