题目大意
对于一个无向图,边有边权。对于每组询问u、v,如果可以找到两条从u到v不相交的路径,输出路径上边权最大值的最小值。
维护边双
我们考虑到,如果按照边权从小到大加边,第一次使得u和v处在同一个边双联通分量时我就求得了答案。
考虑一个很强的方法,用LCT或启发式合并维护森林,动态维护双连通分量。
因为不可能去持久化+二分求询问,我们可以再思考,每次将两个边双合并意味着一次集合合并,我们建一个新点,让原先边双对应点连向该新点,边权即为当前枚举边的边权。
只要做完一次过程,可以发现如果u到v有答案,必定是求u到v在新树(易证建出的新图是树)上树路径边权的最大值,这是个经典套路。
更简单的维护
维护森林是很麻烦的,但是注意到u和v联通才能有答案。
何不先做一次最小生成树(最小生成树也是按边权从小到大插入的!),再依次加入非树边。
这样的好处是,我们有了一颗静态树,不需要再用高级方法维护森林了!
现在我们只需要并查集和倍增就够了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,maxm=500000+10;
struct dong{
int x,y,z;
} a[maxm];
int fa[maxn+maxm],f2[maxn],w[maxn],d[maxn+maxm],zjy[maxn+maxm],f[maxn+maxm][25],g[maxn+maxm][25];
int h[maxn+maxm],from[maxm*2],go[maxm*2],nxt[maxm*2],dis[maxm*2];
bool pd[maxm];
int i,j,k,l,r,s,t,n,m,q,tot,top,root;
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;
}
bool cmp(dong a,dong b){
return a.z<b.z;
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int getfa2(int x){
return f2[x]?f2[x]=getfa2(f2[x]):x;
}
void add(int x,int y,int z){
go[++tot]=y;
from[tot]=x;
dis[tot]=z;
nxt[tot]=h[x];
h[x]=tot;
}
void dfs(int x,int y){
f[x][0]=y;
d[x]=d[y]+1;
int t=h[x];
while (t){
if (go[t]!=y){
g[go[t]][0]=dis[t];
dfs(go[t],x);
}
t=nxt[t];
}
}
void dg(int x){
int t=h[x];
while (t){
d[go[t]]=d[x]+1;
dg(go[t]);
t=nxt[t];
}
}
int lca(int x,int y){
if (d[x]<d[y]) swap(x,y);
if (d[x]!=d[y]){
int j=zjy[d[x]];
while (j>=0){
if (d[f[x][j]]>=d[y]) x=f[x][j];
j--;
}
}
if (x==y) return x;
int j=zjy[d[x]];
while (j>=0){
if (f[x][j]!=f[y][j]){
x=f[x][j];
y=f[y][j];
}
j--;
}
return f[x][0];
}
int getmx(int u,int v){
int w=lca(u,v),t=0,j;
j=zjy[d[u]];
while (j>=0){
if (d[f[u][j]]>=d[w]){
t=max(t,g[u][j]);
u=f[u][j];
}
j--;
}
j=zjy[d[v]];
while (j>=0){
if (d[f[v][j]]>=d[w]){
t=max(t,g[v][j]);
v=f[v][j];
}
j--;
}
return t;
}
int main(){
freopen("city.in","r",stdin);freopen("city.out","w",stdout);
n=read();m=read();q=read();
fo(i,1,n) w[i]=read();
fo(i,1,m){
j=a[i].x=read();k=a[i].y=read();
a[i].z=abs(w[j]-w[k]);
}
sort(a+1,a+m+1,cmp);
fo(i,1,m){
j=a[i].x;k=a[i].y;
if (getfa(j)!=getfa(k)){
pd[i]=1;
add(j,k,a[i].z);add(k,j,a[i].z);
fa[getfa(j)]=getfa(k);
}
}
dfs(1,0);
fo(i,1,n+m) zjy[i]=floor(log(n)/log(2));
fo(j,1,zjy[n])
fo(i,1,n){
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]);
}
fo(i,1,n) fa[i]=f2[i]=0;
top=n;
tot=0;
fo(i,1,n) h[i]=0;
fo(i,1,m)
if (!pd[i]){
j=a[i].x;k=a[i].y;
if (getfa(j)==getfa(k)) continue;
++top;
l=getmx(j,k);
r=lca(j,k);
t=j;
while (d[t]>=d[r]){
if (d[getfa2(t)]<=d[r]) break;
add(top,getfa(t),max(l,a[i].z));
fa[getfa(t)]=top;
t=f[getfa2(t)][0];
}
t=k;
while (d[t]>=d[r]){
if (d[getfa2(t)]<=d[r]) break;
add(top,getfa(t),max(l,a[i].z));
fa[getfa(t)]=top;
t=f[getfa2(t)][0];
}
add(top,getfa(r),max(l,a[i].z));
fa[getfa(r)]=top;
t=j;
while (d[t]>=d[r]){
if (d[getfa2(t)]<=d[r]) break;
s=getfa2(t);
f2[getfa2(t)]=f[s][0];
t=f[s][0];
}
t=k;
while (d[t]>=d[r]){
if (d[getfa2(t)]<=d[r]) break;
s=getfa2(t);
f2[getfa2(t)]=f[s][0];
t=f[s][0];
}
}
fo(i,1,top) d[i]=0;
fo(i,1,tot)
f[go[i]][0]=from[i],g[go[i]][0]=dis[i],d[go[i]]++;
fo(j,1,zjy[top])
fo(i,1,top){
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]);
}
fo(i,1,top)
if (!d[i]) dg(i);
fo(i,1,q){
j=read();k=read();
if (getfa(j)!=getfa(k)) printf("infinitely\n");
else printf("%d\n",getmx(j,k));
}
}