51nod 1743 雪之国度 Kruskal+倍增+并查集

19 篇文章 0 订阅
17 篇文章 0 订阅

题意

雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路。
雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为Wi。
如果城市u和v之间有一条道路,那么只要此刻雪之女王的能量不小于|Wu-Wv|,这条道路就是安全的。
如果城市u和v之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。
最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对Q对城市进行调查。
对于第j对城市uj和vj,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。
3<=N<=100000, 3<=M<=500000, 1<=Q<=100000, 每一座城市的能量Wi满足0<=Wi<=200000.

分析

考虑如果我们从小到大加边的话,一定是先把最小生成树建出来,然后再把生成树上的某条链缩起来。
那么我们就先把mst搞出来,对于每条不在mst上的边,我们将其在mst上的整条路径缩起来,这个可以用并查集来实现。每将两个集合缩起来,就在这两个集合的根节点之间连一条权值为当前边权的边。最后处理询问的时候,答案就是新树两点路径间边权的最大值。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=100005;
const int inf=1000000000;

int n,m,q,cnt,last[N],tot,fa[N][20],mx[N][20],dep[N],f[N],w[N];
struct edge{int to,next,w;}e[N*3];
pair<int,pair<int,int> > a[N*5],b[N];
bool vis[N*5];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int find(int x)
{
    if (f[x]==x) return x;
    else return f[x]=find(f[x]);
}

void addedge(int u,int v,int w)
{
    e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].w=w;e[cnt].next=last[v];last[v]=cnt;
}

void dfs1(int x)
{
    dep[x]=dep[fa[x][0]]+1;
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x][0]) continue;
        fa[e[i].to][0]=x;
        dfs1(e[i].to);
    }
}

void dfs2(int x)
{
    dep[x]=dep[fa[x][0]]+1;
    for (int i=1;i<=16;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for (int i=1;i<=16;i++) mx[x][i]=max(mx[x][i-1],mx[fa[x][i-1]][i-1]);
    for (int i=last[x];i;i=e[i].next)
    {
        if (e[i].to==fa[x][0]) continue;
        fa[e[i].to][0]=x;
        mx[e[i].to][0]=e[i].w;
        dfs2(e[i].to);
    }
}

void kruskal()
{
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        a[i]=make_pair(abs(w[x]-w[y]),make_pair(x,y));
    }
    sort(a+1,a+m+1);
    for (int i=1;i<=m;i++)
    {
        int x=a[i].second.first,y=a[i].second.second;
        if (find(x)!=find(y)) f[find(x)]=find(y),addedge(x,y,0),vis[i]=1;
    }
}

void build()
{
    dfs1(1);
    for (int i=1;i<=n;i++) if (!dep[i]) dfs1(i);
    for (int i=1;i<=n;i++) f[i]=i;
    int tot=0;
    for (int i=1;i<=m;i++)
    {
        if (vis[i]) continue;
        int x=find(a[i].second.first),y=find(a[i].second.second),z=a[i].first;
        while (x!=y)
        {
            if (dep[x]<dep[y]) swap(x,y);
            b[++tot]=make_pair(z,make_pair(x,find(fa[x][0])));
            f[x]=find(fa[x][0]);x=find(x);
        }
    }
    for (int i=1;i<=n;i++) last[i]=dep[i]=fa[i][0]=0;
    cnt=0;
    for (int i=1;i<=tot;i++) addedge(b[i].second.first,b[i].second.second,b[i].first);
    for (int i=1;i<=n;i++) if (!dep[i]) dfs2(i);
}

void solve()
{
    while (q--)
    {
        int x=read(),y=read(),ans=0;
        if (dep[x]<dep[y]) swap(x,y);
        for (int i=16;i>=0;i--)
            if (dep[fa[x][i]]>=dep[y]) ans=max(ans,mx[x][i]),x=fa[x][i];
        for (int i=16;i>=0;i--)
            if (fa[x][i]!=fa[y][i])
            {
                ans=max(ans,max(mx[x][i],mx[y][i]));
                x=fa[x][i];y=fa[y][i];
            }
        if (x!=y) ans=max(ans,max(mx[x][0],mx[y][0])),x=fa[x][0],y=fa[y][0];
        if (!x) puts("infinitely");
        else printf("%d\n",ans);
    }
}

int main()
{
    n=read();m=read();q=read();
    for (int i=1;i<=n;i++) w[i]=read();
    kruskal();
    build();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值