Solution [NOI204] Enchanted Forest
Title effect: Given an undirected graph, each edge has two weights \ (A \) , \ (B \) , a request from \ (1 \) to the \ (n-\) path, such that \ ( max \ {A \} + max \ {B \} \) minimum
Analysis: There are two restrictions is not good to do, we consider the enumeration \ (A \) limit \ (A_ {limit} \)
Each time the \ (A \ leq A_ {limit } \) side of FIG was added, and we just need to find a path such that \ (max \ {B \} \) minimum, \ (ANS = A_ limit} + { max \ {B \} \)
Then sets dichotomous + and set it to check connectivity determination
The above is the mouth Hu algorithm, the complexity of the explosion
Then we consider the optimization:
First enumerate \ (A_ {limit} \) , because we are not the last to join the side of the limits, so you can directly follow the edge \ (A \) sorted and then edged
As maintenance \ (max \ {B \} \) , we find the edge is of no use, we only need to \ (B \) greedy bordered until \ (. 1 \) , \ (n-\) Unicom up
It sounds a bit difficult to maintain, but in fact is a \ (Kruskal \) minimum spanning tree.
The question then is the dynamic bordered maintenance \ (MST \)
We found add up to \ (n-1 \) edges will appear after the ring, we just need to put the edges added to the list, the largest side edge weights can be deleted
Before edging ring is not a forest, considered a dynamic tree, we use \ (LCT \) to maintain itMostly I just this
Plain \ (LCT \) processing point right, we use a dot to represent the side, then that \ (LCT \) a template
Code:
#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
const int maxm = 1e5 + 100;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
namespace LCT{//LCT板子
#define ls ch[x][0]
#define rs ch[x][1]
const int maxn = 2e5;
int ch[maxn][2],faz[maxn],v[maxn],val_mx[maxn],rev[maxn];//注意:我们不仅要维护最大值,还要维护最大边权的边的编号,v是边权(点权),val_mx就是LCT维护的值
inline int isroot(int x){return ch[faz[x]][0] != x && ch[faz[x]][1] != x;}
inline int chk(int x){return ch[faz[x]][1] == x;}
inline void pushup(int x){//上传信息
val_mx[x] = x;
if(ls && v[val_mx[ls]] > v[val_mx[x]])val_mx[x] = val_mx[ls];
if(rs && v[val_mx[rs]] > v[val_mx[x]])val_mx[x] = val_mx[rs];
}
inline void pushr(int x)//一下均为模板{swap(ls,rs);rev[x] ^= 1;}
inline void pushdown(int x){
if(!rev[x])return;
if(ls)pushr(ls);
if(rs)pushr(rs);
rev[x] = 0;
}
inline void rotate(int x){
int y = faz[x],z = faz[y],k = chk(x),w = ch[x][!k];
ch[y][k] = w;if(w)faz[w] = y;
if(!isroot(y))ch[z][chk(y)] = x;faz[x] = z;
ch[x][!k] = y;faz[y] = x;
pushup(y),pushup(x);
}
inline void splay(int x){
static int st[maxn];
int y = x,top = 0;
st[++top] = y;
while(!isroot(y))st[++top] = y = faz[y];
while(top)pushdown(st[top--]);
while(!isroot(x)){
int y = faz[x];
if(!isroot(y))
chk(x) == chk(y) ? rotate(y) : rotate(x);
rotate(x);
}
}
inline void access(int x){
for(int y = 0;x;y = x,x = faz[x])
splay(x),ch[x][1] = y,pushup(x);
}
inline int findroot(int x){
access(x),splay(x);
while(ch[x][0])pushdown(x),x = ch[x][0];
splay(x);
return x;
}
inline void makeroot(int x){access(x),splay(x),pushr(x);}
inline void split(int x,int y){makeroot(x),access(y),splay(y);}
inline void link(int x,int y){makeroot(x);if(findroot(y) != x)faz[x] = y;}
inline void cut(int x,int y){makeroot(x);if(findroot(y) == x && faz[y] == x && !ch[y][0])faz[y] = ch[x][1] = 0,pushup(x);}
#undef ls
#undef rs
}using namespace LCT;
struct Edge{
int from,to,dista,distb,id;//id为原来编号
bool operator < (const Edge &rhs)const{
return (dista == rhs.dista) ? (distb < rhs.distb) : (dista < rhs.dista);
}
}org[maxm],Edges[maxm];//org为原来边数组,排序后打乱顺序
int n,m,ans = 0x7fffffff;
int main(){
n = read(),m = read();
for(int i = 1;i <= m;i++){
Edges[i].from = read();
Edges[i].to = read();
Edges[i].dista = read();
val_mx[n + i] = n + i;
v[n + i] = Edges[i].distb = read();
Edges[i].id = i;
org[i] = Edges[i];
}
sort(Edges + 1,Edges + 1 + m);
for(int i = 1;i <= m;i++){
Edge &e = Edges[i];
if(e.from == e.to)continue;
if(findroot(e.from) == findroot(e.to)){
split(e.from,e.to);
int mx = val_mx[e.to];//得到最大边的编号,其实就是点的编号
if(e.distb > v[mx])continue;//如果加进去马上又要删掉还不如不加
cut(mx,org[mx - n].from);
cut(mx,org[mx - n].to);
}
link(n + e.id,e.from),link(n + e.id,e.to);
if(findroot(1) == findroot(n))ans = min(ans,Edges[i].dista + (split(1,n),v[val_mx[n]]));
}
return printf("%d\n",ans == 0x7fffffff ? -1 : ans),0;
}