<h1>题目简述</h1>
给定 $N$ 个点 $M$ 条边的无向图,每条边有两个权值 $a$ 与 $b$ 。求一条 $1$ 到 $n$ 的路径使得路径经过边的最大 $a$ 与最大 $b$ 的和最小。无法到达输出 $-1$。
$ n \leq 50000,m \leq 100000$。
<h1>解题报告</h1>
乱入:这个题目只需要枚举 a 然后动态加边跑 SPFA 就可以了。
请不要用一个时间复杂度是 $O(nm)$ 的算法来解决问题。
好我们开始正经的做题。
首先如果只有一个权值的话非常好做就是 $1$ 到 $n$ 的最小瓶颈生成树。但是两个权值怎么做呢?我们考虑对 $a$ 排序,枚举我们的答案 $a'$ ,每次加入满足 $a \leq a'$ 的边即可,如果构成了一个树我们就进行统计答案,如果加边的时候已经连通就贪心地删掉 $b_i$ 最大的边。
<h1>代码</h1>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdio> #include <vector> #include <cstdlib> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define U unsigned #define lc cx #define rc cx #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c) #define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c) #define CLR(i,a) memset(i,a,sizeof(i)) #define BR printf("--------------------n") #define DEBUG(x) std::cerr << #x << '=' << x << std::endl const int MAXN = 300000+5; struct Edge{ int u,v,a,b; bool operator < (const Edge &other) const { return a < other.a; } }e[MAXN<<1]; int cMAXN,val[MAXN],tag[MAXN],f[MAXN]; int max[MAXN],pos[MAXN]; inline bool nroot(int x){ return c[f[x]][0] == x || c[f[x]][1] == x; } inline void pushup(int x){ if(val[x] > max[lc] && val[x] > max[rc]){ max[x] = val[x];pos[x] = x; } else{ max[x] = std::max(max[lc],max[rc]); pos[x] = max[lc] > max[rc] ? pos[lc] : pos[rc]; } } inline void reverse(int x){ std::swap(lc,rc);tag[x] ^= 1; } inline void pushdown(int x){ if(tag[x]){ if(lc) reverse(lc); if(rc) reverse(rc); tag[x] = 0; } } inline void rotate(int x){ int y = f[x],z = f[y]; int k = cy == x,w = cx; if(nroot(y)) cz[1]==y] = x; cx = y;cy = w; f[w] = y; f[y] = x;f[x] = z; pushup(y); } int st[MAXN]; inline void splay(int x){ int y = x,z;st[z=1]=y; while(nroot(y)) st[++z] = y = f[y]; while(z) pushdown(st[z--]); while(nroot(x)){ y = f[x];z = f[y]; if(nroot(y)) rotate((cy==x)^(cz==y) ? x : y); rotate(x); } pushup(x); } inline void access(int x){ for(int y=0;x;x = f[y = x]){ splay(x);rc = y;pushup(x); } } inline void makeroot(int x){ access(x);splay(x);reverse(x); } inline void split(int x,int y){ makeroot(x);access(y);splay(y); } inline int findroot(int x){ access(x);splay(x); while(lc) pushdown(x),x=lc; return x; } inline void link(int x,int y){ makeroot(x);f[x] = y; } inline void cut(int x,int y){ makeroot(x); if(findroot(y) == x && f[x] == y && !rc){ f[x] = cy = 0;pushup(y); } } inline bool connected(int x,int y){ int fx = findroot(x),fy = findroot(y); return fx == fy; } inline void calc(int x,int y,int &mx,int &id){ split(x,y);mx = max[y];id = pos[y]; } int N,M; int main(){ scanf("%d%d",&N,&M); FOR(i,1,M) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b); int ans = INT_MAX; std::sort(e+1,e+M+1); FOR(i,1,N+M){ max[i] = val[i] = 0;pos[i] = i; if(i > N) max[i] = val[i] = e[i-N].b; } FOR(i,1,M){ int mx,id,x = e[i].u,y = e[i].v; if(connected(x,y)){ calc(x,y,mx,id); if(mx > e[i].b){ cut(id,e[id-N].u);cut(id,e[id-N].v); link(N+i,x);link(N+i,y); } }else{ link(N+i,x);link(N+i,y); } if(connected(1,N)){ calc(1,N,mx,id); ans = std::min(ans,mx+e[i].a); } if(e[i].a > ans) break; } printf("%dn",ans==INT_MAX ? -1 : ans); return 0; } /* 4 5 1 2 19 1 2 3 8 12 2 4 12 15 1 3 17 8 3 4 1 17 */
2 comments
这是个板子题啊 $qwq$
RainAir 怒切 NOI 神仙题!