题目链接
本文参考:https://blog.csdn.net/qq_45778406/article/details/114003999
根据题目和样例可知,边和点都是可以反复使用的,所以可以通过来反复走一条边来改变答案。
我们假设当前我们认定的美丽值为x,用1来表示权值大于等于该美丽值的点,用0表示权值小于美丽值的点。
假设图中存在一条这样的路径
…11…
即有两个权值大于等于美丽值的结点直接连接,那么我们就可以在这两点之间不断来回走动,使得我们当前认定的美丽值不处于K/2+1的位置上,显然美丽值会被往小的方向推动。
假设图中只有如下的路径
…01…
就是两种点不断交替。显然如果我们在这两个点之间来回走动,会在美丽值左边增加一个点,会在美丽值右边增加一个点,美丽值的相对位置并没有发生改变。
…00…
最后一种路径,读者自行考虑
所以我们使用二分的方法来确定当前的美丽值。也就是说根据美丽值来对图进行分类然后遍历。
考虑清楚之后结论如下
1.出现11的情况,说明当前美丽值过小
2.出现00的情况,说明当前美丽值过大
3.出现01的情况
01情况还需要进一步细分
根据起点和终点的选择,有如下四种情况
101010
010101
10101
01010
其中前两种情况在排序之后都是000111,位于k/2+1位置上的为1,说明当前美丽值过小
第三种排序之后为00111,也是过小
第四种排序之后为00011,则是过大
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int t,n,m,st,en,tot,maxx;
int s[200005],head[200005],vis[200005],a[200005];
struct node {
int v,next;
};
struct node edge[400005];
void init(){
tot=0;maxx=0;
for(int i=1;i<=n;i++){
head[i]=-1;
s[i]=i;
}
}
int find(int now){
if(s[now]==now)return now;
else return s[now]=find(s[now]);
}
void add(int u,int v){
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot;
tot++;
}
void dfs(int now,int val){
vis[now]=1;
for(int i=head[now];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(vis[v])continue;
if(max(a[now],a[v])>=val)dfs(v,val);//判断是否为01交替,如果为00,则无法到达终点
}
}
int work(int val){
for(int i=1;i<=n;i++){// 11判断
if(a[i]>=val&&(find(i)==find(st))){
for(int j=head[i];j!=-1;j=edge[j].next){
if(a[edge[j].v]>=val)return 1;
}
}
}
if(a[st]<val&&a[en]<val)return 0;// 首尾均为0判断
for(int i=1;i<=n;i++)vis[i]=0;
dfs(st,val);
return vis[en];
}
int main(){
#ifdef LOCAL
freopen(".\\a.in", "r", stdin);
#endif
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d",&n,&m,&st,&en);
init();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
maxx=max(maxx,a[i]);
}
for(int i=1;i<=m;i++){
int u,v,fu,fv;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
fu=find(u);fv=find(v);
if(fu!=fv){
s[fu]=fv;
}
}
if(find(st)!=find(en)){
printf("NO\n");continue;
}
int l=1;
int r=1e9+10;
while(l<r){
int mid=(l+r+1)/2;
if(work(mid)==1)l=mid;
else r=mid-1;
}
printf("YES\n%d\n",l);
}
return 0;
}
/*
二分美丽值
然后我们将点分成0和1
0表示小于当前美丽值的点
1表示大于等于当前美丽值的点
然后可以很明显的看出如果路径上出现11
也就是两个大于等于的点,然后我们就可以不断地跑这条边,使得当前地美丽值被推走,可见当前美丽值过小
或者如下三种情况,101010,010101,10101,因为这种情况下,无论怎么寻找路径,位于美丽值位置地永远都为1,所以美丽值同样过小
其它情况则是美丽值过大
所以我们先特殊判断11的情况,然后再判断首尾都是0的情况,剩下的方案如果是交错的,那么就是美丽值过小的情况
*/