【问题描述】
有 n 个兵营里驻扎着士兵,由于兵营间没有路,士兵不能相互增援。现在工程队每天
可以修建一条连接两个兵营的双向道路。现在指挥官询问,对于一个兵营 u,最早什么时候
可以增援兵营 v,即两个兵营之间有连通的路径。
【输入数据】
第一行两个整数 N,M,表示兵营的数量和道路的数量,兵营从 1-N 编号。
接下来 m 行,每行两个整数 x,y,表示会按顺序修建兵营 x 和兵营 y 之间的道路,所有道
路都是双向的,数据可能存在重边。
接下来一行一个整数 Q,表示有 Q 个询问。
接下来 Q 行每行两个整数 u,v,表示询问兵营 u 最早在什么时候能出兵增援兵营 v。
【输出数据】
每行一个整数 ti,表示对于第 i 个询问,最早在 ti 天后能满足要求。若始终无法满足,
则输出-1。
【输入样例 1】
4 3
1 2
1 3
2 3
3
1 2
2 3
1 4
【输出样例 1】
1
2
-1
【数据范围】
对于 30%的数据,1 N 100,1 M 1000,1 Q 1000。
对于 100%的数据,1 N 10^5,1 M 10^6,1 Q 10^5。
———————————————分割の线———————————————————————
来篇有趣的题解……
【分析】
N,Q很大,且M更大,且最大的M是N的10倍。但仔细思考不难发现,只需要最少N-1条边即可以使所有兵营连通,且要求两两兵营之间尽早连通。可以用最小生成树的Kruskal算法来完成加边(边的权值即为边的序号,所以无需排序),同时注意用并查集维护两者是否为同一集合。此处LCA使用倍增算法(易于实现),加边完成后进行p、d数组的预处理操作。此处引进一个Max数组,定义与p数组相同,表示第i个数向上2^j个点所经过的2^j条边的最大值。在lca倍增的爬树操作时,更新p同时更新Max,返回值输出最大值。
本期重点:两点间最早连通—->并查集+LCA倍增求树上RMQ
详见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010;
struct node
{
int v,nxt,c;
}edge[N<<1];
int head[N],tot=0,cnt=0;
//picture
int f[N];
//FF-set
int d[N],p[N][20],Max[N][20];
//倍增
int n,m,q,large=1;
int find(int x)
{
if(x!=f[x])f[x]=find(f[x]);
return f[x];
}
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
void add_edge(int x,int y,int c)
{
edge[tot].nxt=head[x];
edge[tot].v=y;
edge[tot].c=c;
head[x]=tot++;
}
void dfs(int u,int dep)
{
d[u]=dep;
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(!d[v])
{
Max[v][0]=edge[i].c;
p[v][0]=u;
dfs(v,dep+1);
}
}
return ;
}
void solve()
{
for(int i=1;i<=n;i++)
if(!d[i])
{
p[i][0]=i;
Max[i][0]=0;
dfs(i,1);
}
large=0;
while(1<<large<=n) large++;
large--;
for(int j=1;j<=large;j++)
for(int i=1;i<=n;i++)
{
p[i][j]=p[p[i][j-1]][j-1];
Max[i][j]=max(Max[i][j-1],Max[p[i][j-1]][j-1]);
}
return ;
}
int lca(int x,int y)
{
int X=0,Y=0;
if(d[x]>d[y]) swap(x,y);
for(int i=large;i>=0;i--)
if(d[y]-(1<<i)>=d[x])
{
Y=max(Y,Max[y][i]);
y=p[y][i];
}
if(x==y) return Y;
for(int i=large;i>=0;i--)
if(p[x][i]!=p[y][i])
{
X=max(X,Max[x][i]);
Y=max(Y,Max[y][i]);
x=p[x][i];
y=p[y][i];
}
X=max(X,Max[x][0]);
Y=max(Y,Max[y][0]);
return max(X,Y);
}
int main()
{
freopen("build.in","r",stdin);
freopen("build.out","w",stdout);
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
memset(Max,0,sizeof(Max));
memset(head,-1,sizeof(head));
n=read(),m=read();
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
x=read(),y=read();
if(find(x)==find(y))continue;
f[find(x)]=find(y);
add_edge(x,y,i);
add_edge(y,x,i);
cnt++;
}
solve();
q=read();
for(int i=1;i<=q;i++)
{
int x,y;
x=read(),y=read();
if(find(x)!=find(y))
{
printf("-1\n");
continue;
}
printf("%d\n",lca(x,y));
}
return 0;
}