一开始写了个二分a+最短路b,骗了65分,然后改成二分b+最短路a,骗了70分。。发现二分是不对的之后,给答案取min,骗到了90分。出题人太不负责任了。
正解是枚举a,用LCT维护b的最小生成树。
/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 50005, maxm = 100005, maxnode = 200005, inf = 0x3f3f3f3f;
int n, m, fa[maxn];
struct _edge {
int u, v, a, b;
bool operator < (const _edge &x) const {
return a != x.a ? a < x.a : b < x.b;
}
} mp[maxm];
inline int iread() {
int f = 1, x = 0; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return f * x;
}
inline int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int son[maxnode][2], pre[maxnode], mx[maxnode], val[maxnode];
bool rev[maxnode];
int sta[maxnode];
inline bool isroot(int x) {
return son[pre[x]][0] != x && son[pre[x]][1] != x;
}
inline void pushup(int x) {
mx[x] = x;
if(val[mx[son[x][0]]] > val[mx[x]]) mx[x] = mx[son[x][0]];
if(val[mx[son[x][1]]] > val[mx[x]]) mx[x] = mx[son[x][1]];
}
inline void pushdown(int x) {
if(rev[x]) {
for(int i = 0; i < 2; i++) {
rev[son[x][i]] ^= 1;
swap(son[son[x][i]][0], son[son[x][i]][1]);
}
rev[x] = 0;
}
}
inline void maintain(int x) {
int top = 0;
for(sta[++top] = x; !isroot(x); x = pre[x]) sta[++top] = pre[x];
for(; top; pushdown(sta[top--]));
}
inline void rotate(int x) {
int y = pre[x], z = pre[y], type = son[y][1] == x;
pre[son[y][type] = son[x][!type]] = y;
pre[x] = z;
if(!isroot(y)) son[z][son[z][1] == y] = x;
pre[son[x][!type] = y] = x;
pushup(y); pushup(x);
}
inline void splay(int x) {
maintain(x);
while(!isroot(x)) {
int y = pre[x], z = pre[y];
if(isroot(y)) rotate(x);
else if(son[z][1] == y ^ son[y][1] == x) rotate(x), rotate(x);
else rotate(y), rotate(x);
}
}
inline void access(int x) {
for(int y = 0; x; x = pre[y = x]) {
splay(x);
son[x][1] = y;
pushup(x);
}
}
inline void makeroot(int x) {
access(x); splay(x); rev[x] ^= 1;
swap(son[x][0], son[x][1]);
}
inline void link(int x, int y) {
makeroot(x); pre[x] = y;
}
inline void cut(int x, int y) {
makeroot(x); access(y); splay(y);
son[y][0] = pre[x] = 0;
}
inline int query(int x, int y) {
makeroot(x); access(y); splay(y);
return mx[y];
}
int main() {
n = iread(); m = iread();
for(int i = 1; i <= m; i++) mp[i] = (_edge){iread(), iread(), iread(), iread()};
for(int i = 1; i <= n; i++) fa[i] = i;
sort(mp + 1, mp + 1 + m);
int ans = inf;
for(int i = 1; i <= m; i++) {
int u = mp[i].u, v = mp[i].v; val[i + n] = mp[i].b; mx[i + n] = i + n;
if(find(u) != find(v)) {
link(u, i + n), link(i + n, v);
fa[find(u)] = find(v);
} else {
int x = query(u, v);
if(val[x] > mp[i].b) {
cut(mp[x - n].u, x); cut(x, mp[x - n].v);
link(u, i + n); link(i + n, v);
}
}
if(find(1) == find(n)) ans = min(ans, mp[i].a + val[query(1, n)]);
}
if(ans == inf) ans = -1;
printf("%d\n", ans);
return 0;
}