题目大意:给出一个无向图,每条边i有两个值ai、bi。要求出一条从1到n的路,使得路上经过的a的最大值(设为A)与b的最大值(设为B)的和尽量小。
思路:容易想到逐步往图中加入边的做法,但是这一题存在两个关键值,所以我们还需要利用一点点贪心的思想来权衡答案。
我们考虑按a值从小到大加入每条边i,每一次求出A不超过ai的情况下,1的n的路径上B的最小值。
那么我们只需要维护以b值为权值的一棵包含最优解的树就可以了:
当前加入边i,如果没有形成环,那么直接连接xi,yi。如果形成环,找到环上b值最大的边,将它删去即可。
这样,我们每一次询问1到n在树上的路径上的B值就可以了。
这棵不断删边、连边的树可以用LCT来维护,大功告成。
注意:1.LCT有一些需要注意的地方,比如updata不能少,比如rotate有一点点区别于splay等;
2.这题有重边、自环!
这些注意点都已在程序中用attention标出。
#include <cstdio>
#include <algorithm>
#include <cstring>
#define lf ch[x][0]
#define rg ch[x][1]
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=2e5+5,M=1e5+5;
int n,m,i,u,v,fx,fy,wh,tmp,ans;
struct arr{
int x,y,a,b;
}e[M];
bool cmp(arr A,arr B) {
return A.a<B.a;
}
struct lct{
int i,tp,cs,w[N],ms[N],ch[N][2],stk[N],fa[N],rev[N];
inline bool isroot(int x) { return fa[x]==0 || ((ch[fa[x]][0]!=x)&&(ch[fa[x]][1]!=x));}
inline bool side(int x) { return ch[fa[x]][1]==x; }
inline void updata(int x) {
ms[x]=x;
if (lf && w[ms[lf]]>w[ms[x]]) ms[x]=ms[lf];
if (rg && w[ms[rg]]>w[ms[x]]) ms[x]=ms[rg];
}
void push(int x) { if (!rev[x]) return;
rev[x]=0; swap(ch[x][0],ch[x][1]);
rev[lf]^=1; rev[rg]^=1;
}
void rotate(int x)
{
int f=fa[x],gf=fa[fa[x]],sd=side(x),fsd=side(f),son=ch[x][sd^1],flg=isroot(fa[x]);
fa[f]=x; fa[x]=gf; if (!flg) ch[gf][fsd]=x; //attention >_<
fa[son]=f; ch[f][sd]=son; ch[x][sd^1]=f;
updata(f); updata(x);
}
void splay(int x)
{
int y=x;
for(tp=0;!isroot(y);y=fa[y]) stk[++tp]=y;
for (stk[++tp]=y;tp;tp--) push(stk[tp]); //attention ._.
while (!isroot(x))
{
if (isroot(fa[x])) rotate(x);
else {
if (side(x)==side(fa[x])) rotate(fa[x]),rotate(x);
else rotate(x),
rotate(x);
}
}
}
void access(int x)
{
int y=0;
while (1) {
splay(x);
ch[x][1]=y; updata(x); //attention ._.
y=x; x=fa[x];
if (!x) return ;
}
}
void makeroot(int x) {
access(x); splay(x); rev[x]^=1;
}
void link(int u,int v) {
makeroot(u); fa[u]=v;
}
void cut(int u,int v) {
makeroot(u); access(v); splay(v);
ch[v][0]=0; fa[u]=0; updata(v); //attention ._.
}
int query(int u,int v) {
makeroot(v); access(u); splay(v); return ms[v];
}
bool smt(int u,int v) {
makeroot(u); makeroot(v);
return !isroot(u);
}
}tr;
void read(int &ret)
{
char ch; ret=0;
for (ch=getchar();ch<'0' || ch>'9';ch=getchar());
for (;ch>='0' && ch<='9';ch=getchar()) ret=ret*10+ch-'0';
}
int main()
{
read(n); read(m);
rep(i,1,m) read(e[i].x),read(e[i].y),read(e[i].a),read(e[i].b);
sort(e+1,e+1+m,cmp);
rep(i,1,n+m) tr.ms[i]=i;
rep(i,n+1,n+m) tr.w[i]=e[i-n].b;
ans=1e9;
rep(i,1,m)
{
tmp=e[i].a; u=e[i].x; v=e[i].y; if (u==v) continue; //attention-_-
if (!tr.smt(u,v)) {
tr.link(u,n+i); tr.link(n+i,v);
}
else {
wh=tr.query(u,v);
if (tr.w[wh]>e[i].b)
{
tr.cut(e[wh-n].x,wh); tr.cut(e[wh-n].y,wh);
tr.link(u,n+i); tr.link(n+i,v);
}
}
if (tr.smt(1,n))
{
tmp+=tr.w[tr.query(1,n)];
ans=min(ans,tmp);
}
}
if (ans==1e9) ans=-1;
printf("%d\n",ans);
return 0;
}