【NOI2014]魅惑の森
説明
マップ、各エッジ所与\(Iは\)する権利((a_iを、B_i)を\)\探し、\(1 \ SIM N \)最小にするパス、\(\ MAX_を{私に\ P } {a_iを} + \ MAX_ { iはPで\} {b_i} \)
解決
我々は、最大定義した場合、\(b_i \)を、そしてパスがためでなければなりません\(a_iを\)右にスパニングツリーを最小限に。
証明。パスがエッジないMST、この非ツリーエッジがツリーと共に環を形成しなければならないを持っていると仮定し、反対の証拠を考慮し、このエッジに低減することができるスパニングツリー上のリングの反対側のいずれか交換することは明らかです矛盾の最小スパニングツリーの重みと条件。
この構成アルゴリズムの性質を持ちます。我々の大小列挙に\(b_i \)最大は、今度は、ツリーを加え、LCTとに維持(a_iを\)\エッジ右に最小スパニングツリーを。
最小スパニングツリーを維持する方法は非常に簡単です。ときに、新しいエッジLCTは、最大利用・チェーン・ツリーを維持する\((U、V、W )\) 私たちは木のチェーンよりも、この辺を考慮した場合、追加された\(uは、SIMのV \を\)その後、小さな最大値、それ最大値のエッジに置き換えます。したがって、最大値の位置とLCTの最大値を維持する必要性。
LCTのメンテナンスの右側を使用して、右側にポイントを割り当てする権利をポイント・ツー・エッジを構築するのが一般的ですが、本当のポイントは、ポイントを持っている権利ではありません。基本的な情報のエッジを維持しながら。
以下に記載のより具体的な実装であります
この問題では、我々は、番号を使用することができる\(1 \ SIMのn \)番号で、実際のポイントを表すために点を(N + 1 \ SIM N + m個の\)\ ドットは、エッジを表現する構造体の配列で、一方中((U、V、W) \)\ 右側の形で記録する基本的な情報。いいえサイドノートをに従って使用することはできません\(b_i \)キーワードランキングに置き換えられます。
LCTの実装レベルでは、我々は暫定的に、最大のポイントとして全体のポイント数右ポイントと最大のツリーチェーンを維持する権利をそれをすべて無視することができます。
私たちが照会する必要がある場合には、\(U \ SIM V \)の間のエッジの最大の権利を、私たちは、LCTに依頼する必要があります。
我々は片側を削除する必要がある場合には、我々は唯一の番号を知っている必要があります\(私は\) 、その後、切り離し\((+ nは、u_i I )\) と\((V_I、N + Iを )\)
我々はエッジを追加する必要がある場合、我々は唯一の番号を知っている必要があります\(私は\) 、その後、接続\((+ nは、u_i I )\) と\((V_I、N + Iを )\)
コード
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000000;
int n,m;
struct Edge {
int u,v,w,b;
bool operator < (const Edge &x) const {
return b < x.b;
}
} e[N];
struct LinkCutTree {
int top, q[N], ch[N][2], fa[N], rev[N], mx[N], mp[N], val[N];
inline void pushup(int x) {
mx[0]=mp[0]=0;
mx[x] = max(max(mx[ch[x][0]],mx[ch[x][1]]),val[x]);
if(mx[x] == mx[ch[x][0]])
mp[x]=mp[ch[x][0]];
if(mx[x] == mx[ch[x][1]])
mp[x]=mp[ch[x][1]];
if(mx[x] == val[x])
mp[x]=x;
}
inline void pushdown(int x) {
if(!rev[x])
return;
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
rev[x]^=1;
swap(ch[x][0],ch[x][1]);
}
inline bool isroot(int x) {
return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
}
inline void rotate(int p) {
int q=fa[p], y=fa[q], x=ch[fa[p]][1]==p;
ch[q][x]=ch[p][x^1];
fa[ch[q][x]]=q;
ch[p][x^1]=q;
fa[q]=p;
fa[p]=y;
if(y)
if(ch[y][0]==q)
ch[y][0]=p;
else if(ch[y][1]==q)
ch[y][1]=p;
pushup(q);
pushup(p);
}
inline void splay(int x) {
q[top=1]=x;
for(int i=x; !isroot(i); i=fa[i])
q[++top]=fa[i];
for(int i=top; i; i--)
pushdown(q[i]);
for(; !isroot(x); rotate(x))
if(!isroot(fa[x]))
rotate((ch[fa[x]][0]==x)==(ch[fa[fa[x]]][0]==fa[x])?fa[x]:x);
}
void access(int x) {
for(int t=0; x; t=x,x=fa[x])
splay(x),ch[x][1]=t,pushup(x);
}
void makeroot(int x) {
access(x);
splay(x);
rev[x]^=1;
}
int find(int x) {
access(x);
splay(x);
while(ch[x][0])
x=ch[x][0];
return x;
}
void split(int x,int y) {
makeroot(x);
access(y);
splay(y);
}
void cut(int x,int y) {
split(x,y);
if(ch[y][0]==x)
ch[y][0]=0, fa[x]=0;
}
void link(int x,int y) {
makeroot(x);
fa[x]=y;
}
void setval(int p,int v) {
val[p]=mx[p]=v;
mp[p]=p;
}
int queryv(int p,int q) {
split(p,q);
return mx[q];
}
int queryp(int p,int q) {
split(p,q);
return mp[q];
}
} lct;
bool check(int p,int q) {
return lct.find(p)==lct.find(q);
}
int queryv(int p,int q) {
return lct.queryv(p,q);
}
int queryp(int p,int q) {
return lct.queryp(p,q)-n;
}
void link(int i) {
lct.link(n+i,e[i].u);
lct.link(n+i,e[i].v);
}
void cut(int i) {
lct.cut(n+i,e[i].u);
lct.cut(n+i,e[i].v);
}
int ans = 1e+12;
signed main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].b);
}
sort(e+1,e+m+1);
for(int i=1;i<=n;i++) lct.setval(i, 0);
for(int i=1;i<=m;i++) lct.setval(i+n, e[i].w);
for(int i=1;i<=m;i++) {
if(check(e[i].u,e[i].v)) {
int v=queryv(e[i].u,e[i].v), p=queryp(e[i].u,e[i].v);
if(v > e[i].w) {
cut(p);
link(i);
}
}
else {
link(i);
}
if(check(1,n)) {
ans = min(ans, queryv(1,n) + e[i].b);
}
}
if(ans < (int)1e+12) cout<<ans<<endl;
else cout<<-1<<endl;
}