题意
n个点,m条无向边,q次查询。
从第l条边到第r条边形成的子图G,将G中的点分成两个集合,求两个集合内部的边的最大值(使其尽量小)。
题解
由于倾向于将较大边的两个端点放在同一集合中,将边按长度从大到小排序,在满足边号属于[l,r] 的前提下,分析该边的端点是否在同一集合中,若在,该边长度即为所求,否则继续遍历。
- 带权并查集:
f[i] 表示点 i 的根, re[i] 表示点 i 与 f[i] 的关系 :1为不同集合,0为同一集合 - 寻根: re[x]^=re[f[x]];
- 将两点连接:
f[fu] = fv;
re[fu] = re[u]^re[v]^1;
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<queue>
#define INF 0x3fffffff
using namespace std;
typedef long long ll;
const int MAX_N = 1e3+10;
const int MAX_M = 1e6+10;
int f[MAX_N];
int re[MAX_N];
struct EDGE
{
int u,v,d,id;
}edge[MAX_M];
bool cmp(EDGE A,EDGE B)
{
return A.d>B.d;
}
int findf(int x)
{
int fx=x;
if(x!=f[x])
{
fx=findf(f[x]);
re[x]^=re[f[x]];
}
return f[x] = fx;
}
int main()
{
int n,p,m;
while(scanf("%d%d%d",&n,&m,&p)!=EOF)
{
for(int i=1;i<=m;i++)
{
cin>>edge[i].u>>edge[i].v>>edge[i].d;
edge[i].id=i;
}
sort(edge+1,edge+1+m,cmp);
int flag=0;
while(p--)
{
flag=0;
for(int i=1;i<=n;i++)
{
f[i] = i;
re[i] = 0;
}
int l,r;
cin>>l>>r;
for(int i=1;i<=m;i++)
{
if(edge[i].id<l || edge[i].id>r)
continue;
int u=edge[i].u;
int v=edge[i].v;
int fu = findf(u);
int fv = findf(v);
if(fu == fv)
{
if(re[u] == re[v])
{
cout<<edge[i].d<<endl;
flag=1;
break;
}
}
else
{
f[fu] = fv;
re[fu] = re[u]^re[v]^1;
}
}
if(flag == 0)
cout<<"-1"<<endl;
else
flag=0;
}
}
return 0;
}