51nod1743 JZOJ4899【NOIP2016提高A组集训第17场11.16】雪之国度

Description

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

Data Constraint

对于20%的数据来说,3<=N<=10, 3<=M<=20, 1<=Q<=10
对于另30%的数据来说,Wi=0
对于100%的数据来说,3<=N<=100000, 3<=M<=500000, 1<=Q<=100000, 每一座城市的能量Wi满足0<=Wi<=200000.

Solution

我们先构出一个最小生成树,然后将非树边一个个接上去判断影响。显然,当非树边(x,y)接上时,除了已经连接成团的内部,x到y路径上两两团之间每个点的最小安全大小均为该边的权值。所以我们将(x,y)路径上的所有团全部合并成一个团,并把现在团的顶点向每个原团中的顶点连一条该点权值的边,这样我们就可以保证不影响团内部的情况下而让该团到团外其他点的时候均受该边的影响。这可以用并查集来维护。

团的定义是指并查集为同一点的点的集合

最后对于一个询问,我们只要判断一下(x,y)之间是否在同一团内,假如在,求一下(x,y)路径上的边的最大值即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500005;
struct code{
    int a,b,c;
}a[maxn];
int first[maxn],last[maxn],next[maxn],value[maxn],fa[maxn],fa1[maxn],bz[maxn],f[maxn][20],g[maxn][20],deep[maxn];
int n,i,t,j,k,l,m,q,x,y,num,ln,p,b[maxn];
bool cmp(code x,code y){
    return x.c<y.c;
}
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
void dg(int x,int y){
    int t;f[x][0]=y;deep[x]=deep[y]+1;bz[x]=1;fa1[x]=y;
    for (t=first[x];t;t=next[t]){
        if (last[t]==y)continue;g[last[t]][0]=value[t];
        dg(last[t],x);
    }
}
int lca(int x,int y){
    if (deep[x]<deep[y]) swap(x,y);
    int j,t=0;
    for (j=ln;j>=0;j--)
        if (deep[f[x][j]]>=deep[y]) t=max(t,g[x][j]),x=f[x][j];
    if (x==y) return t;
    for (j=ln;j>=0;j--)
        if (f[x][j]!=f[y][j]) t=max(t,max(g[x][j],g[y][j])),x=f[x][j],y=f[y][j];
    return max(t,max(g[x][0],g[y][0]));
}
int getfather(int x){
    if (x==fa[x]) return x;
    fa[x]=getfather(fa[x]);return fa[x];
} 
int main(){
    //freopen("city.in","r",stdin);freopen("city.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    for(i=1;i<=n;i++) 
        scanf("%d",&b[i]);
    for (i=1;i<=m;i++)
        scanf("%d%d",&a[i].a,&a[i].b),a[i].c=abs(b[a[i].a]-b[a[i].b]);
    sort(a+1,a+m+1,cmp);
    for (i=1;i<=n;i++)
        fa[i]=i;
    for (i=1;i<=m;i++){
        x=getfather(a[i].a);y=getfather(a[i].b);
        if(x!=y) fa[x]=y,b[i]=1,lian(a[i].a,a[i].b,0),lian(a[i].b,a[i].a,0);
        else b[i]=0;
    }
    for (i=1;i<=n;i++)
        fa[i]=i;
    dg(1,0);
    num=0;
    memset(first,0,sizeof(first));
    for (i=1;i<=m;i++)
        if (!b[i]){
            x=getfather(a[i].a);y=getfather(a[i].b);
            while (x!=y){
                if (deep[x]<deep[y]) swap(x,y);
                x=getfather(fa1[x]);
            }
            t=x;
            x=getfather(a[i].a);y=getfather(a[i].b);
            while (x!=y){
                if (deep[x]<deep[y]) swap(x,y);
                lian(t,x,a[i].c);
                fa[x]=t;
                x=getfather(fa1[x]);
            }
        }
    memset(bz,0,sizeof(bz));
    for (i=1;i<=n;i++)
        if (!bz[i]) dg(i,0);
    ln=log(n)/log(2);
    for (j=1;j<=ln;j++)
        for (i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1],g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
    for (j=1;j<=p;j++){
        scanf("%d%d",&x,&y);
        t=getfather(x);k=getfather(y);
        if (t!=k) printf("infinitely\n");
        else{
            t=lca(x,y);
            printf("%d\n",t);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值