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


4396

被折叠的 条评论
为什么被折叠?



