【LCA倍增】修路 build

【问题描述】

有 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;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80302522