[51NOD1743][JZOJ4899]雪之国度

题目大意

给定 n 个点, m 条边的无向图。每个点有点权 wi ,一条边 (x,y) 的边权定义为 |wxwy|
q 询问,每次询问两个点 (x,y) ,如果 x y 之间存在至少两条互不相交(没有重复边)的路径,那么输出这两条路径上边权的最大值(如果有多对路径,选择最小的),否则输出 1

3n105,3m5×105,1q105

题目分析

考虑一种最暴力的做法,按边权从小到大插入边,动态维护边双连通分量。连通分量内的答案可以瞎处理。然而这样很难维护。
怎么办呢?用最小生成树来维护一个联通性,这样然后在树上可以更方便的处理边双内路径的最大权值。我们可能会加入某些边使得它有环,那么环内权值就要变成加入这条边的权值(加入边肯定更大)。但是修改过一次的边我们就不能再修改了,因为显然它达到了条件且不会更优。因此我们使用并查集来将所有修改过的点并在一起,这样修改时就可以暴力沿着并查集父亲修改了。
处理询问的话,倍增查询路径最大值即可。
时间复杂度 O(nlog2n)

代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int buf[30];

void write(int x)
{
    if (x<0) x=-x,putchar('-');
    for (;x;x/=10) buf[++buf[0]]=x%10;
    if (!buf[0]) buf[++buf[0]]=0;
    for (;buf[0];) putchar('0'+buf[buf[0]--]);
}

const int N=100050;
const int M=500050;
const int E=N<<1;
const int LGN=17;

struct edge
{
    int x,y,l;
}e[M];

bool operator<(edge a,edge b){return a.l<b.l;}

int fa[N],deep[N],last[N],w[N];
int f[N][LGN],anc[N][LGN];
int tov[E],nxt[E],len[E];
int n,m,q,tot,lgn,ans;
bool chosen[M];

int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}

void insert(int x,int y,int z){tov[++tot]=y,len[tot]=z,nxt[tot]=last[x],last[x]=tot;}

void dfs(int x)
{
    for (int i=last[x],y;i;i=nxt[i])
        if ((y=tov[i])!=anc[x][0])
            anc[y][0]=x,deep[y]=deep[x]+1,f[y][0]=len[i],dfs(y);
}

void pre()
{
    lgn=trunc(log(n)/log(2));
    for (int j=1;j<=lgn;j++)
        for (int i=1;i<=n;i++)
            anc[i][j]=anc[anc[i][j-1]][j-1],f[i][j]=max(f[i][j-1],f[anc[i][j-1]][j-1]);
}

int adjust(int x,int d)
{
    for (int i=lgn;i>=0;i--)
        if (deep[anc[x][i]]>=d) ans=max(ans,f[x][i]),x=anc[x][i];
    return x;
}

void getans(int x,int y)
{
    ans=0;
    if (deep[x]>deep[y]) swap(x,y);
    y=adjust(y,deep[x]);
    if (x==y) return;
    for (int i=lgn;i>=0;i--)
        if (anc[x][i]!=anc[y][i]) ans=max(ans,max(f[x][i],f[y][i])),x=anc[x][i],y=anc[y][i];
    ans=max(ans,max(f[x][0],f[y][0])),x=anc[x][0],y=anc[y][0];
}

int main()
{
    freopen("city.in","r",stdin),freopen("city.out","w",stdout);
    n=read(),m=read(),q=read();
    for (int i=1;i<=n;i++) w[i]=read(),fa[i]=i;
    for (int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].l=abs(w[e[i].x]-w[e[i].y]);
    sort(e+1,e+1+m);
    for (int i=1,x,y,fx,fy;i<=m;i++)
    {
        fx=getfather(x=e[i].x),fy=getfather(y=e[i].y);
        if (fx!=fy)
        {
            chosen[i]=1;
            fa[fy]=fx;
            insert(x,y,e[i].l),insert(y,x,e[i].l);
        }
    }
    anc[1][0]=0,deep[1]=1,dfs(1);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1,x,y;i<=m;i++)
        if (!chosen[i])
            for (x=getfather(e[i].x),y=getfather(e[i].y);x!=y;x=getfather(x))
            {
                if (deep[x]<deep[y]) swap(x,y);
                f[x][0]=max(e[i].l,f[x][0]);
                fa[x]=anc[x][0];
            }
    pre();
    for (int x,y;q--;)
    {
        x=read(),y=read();
        if (getfather(x)!=getfather(y)||x==y) printf("infinitely\n");
        else getans(x,y),write(ans),putchar('\n');
    }
    fclose(stdin),fclose(stdout);
    return 0;
}

你可能感兴趣的:(并查集,MST,OI,边双连通分量,倍增)