bzoj 3669 魔法森林
题解:先将所有的边按照 a 从小达到排序, 类似于最小生成树的方法, 用并查集维护2个点是否联通, 每次处理一条新的边的时候, 先判断这2个点是否联通, 如果不连通就把这2个点连边,如果这2个点已经联通了,那么如果新加上这条边之后就会和原来的链构成一个环, 我们再去掉这个环上b最大的那条边, 每次处理完一条边就判断一下 1 和 n 的连通性, 然后更新答案。
注意的就是 lct 不好维护边权, 我们把边权转化成点权,每次处理一条边的时候都新开一个节点, 这个节点上有花费,如果要连, 就把这原来边的2个端点都连新的节点。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 4 #define LL long long 5 #define ULL unsigned LL 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define lson l,m,rt<<1 10 #define rson m+1,r,rt<<1|1 11 #define lch(x) tr[x].son[0] 12 #define rch(x) tr[x].son[1] 13 #define max3(a,b,c) max(a,max(b,c)) 14 #define min3(a,b,c) min(a,min(b,c)) 15 typedef pair<int,int> pll; 16 const int inf = 0x3f3f3f3f; 17 const LL INF = 0x3f3f3f3f3f3f3f3f; 18 const LL mod = (int)1e9+7; 19 const int N = 5e5 + 100; 20 struct Node{ 21 int rev, rt; 22 int son[2], pre; 23 int mx, val, id; 24 void init(){ 25 rt = 1; rev = pre = son[0] = son[1] = 0; 26 mx = val = id = 0; 27 } 28 }tr[N]; 29 void Push_Rev(int x){ 30 if(!x) return ; 31 swap(lch(x), rch(x)); 32 tr[x].rev ^= 1; 33 } 34 void Push_Up(int x){ 35 if(!x) return ; 36 tr[x].mx = tr[x].val, tr[x].id = x; 37 if(tr[x].mx < tr[lch(x)].mx) tr[x].mx = tr[lch(x)].mx, tr[x].id = tr[lch(x)].id; 38 if(tr[x].mx < tr[rch(x)].mx) tr[x].mx = tr[rch(x)].mx, tr[x].id = tr[rch(x)].id; 39 } 40 void Push_Down(int x){ 41 if(tr[x].rev){ 42 tr[x].rev = 0; 43 Push_Rev(lch(x)); 44 Push_Rev(rch(x)); 45 } 46 } 47 void Rev(int x){ 48 if(!tr[x].rt) Rev(tr[x].pre); 49 Push_Down(x); 50 } 51 52 void rotate(int x){ 53 if(tr[x].rt) return; 54 int y = tr[x].pre, z = tr[y].pre; 55 int k = (rch(y) == x); 56 tr[y].son[k] = tr[x].son[k^1]; 57 tr[tr[y].son[k]].pre = y; 58 tr[x].son[k^1] = y; 59 tr[y].pre = x; 60 tr[x].pre = z; 61 if(tr[y].rt) tr[y].rt = 0, tr[x].rt = 1; 62 else tr[z].son[rch(z) == y] = x; 63 Push_Up(y); 64 } 65 void Splay(int x){ 66 Rev(x); 67 while(!tr[x].rt){ 68 int y = tr[x].pre, z = tr[y].pre; 69 if(!tr[y].rt){ 70 if(( x == rch(y) ) != (y == rch(z))) rotate(y); 71 else rotate(x); 72 } 73 rotate(x); 74 } 75 Push_Up(x); 76 } 77 void Access(int x){ 78 int y = 0; 79 do{ 80 Splay(x); 81 tr[rch(x)].rt = 1; 82 rch(x) = y; 83 tr[y].rt = 0; 84 Push_Up(x); 85 y = x; 86 x = tr[x].pre; 87 }while(x); 88 } 89 void Make_rt(int x){ 90 Access(x); 91 Splay(x); 92 Push_Rev(x); 93 } 94 void link(int u, int v){ 95 Make_rt(u); 96 tr[u].pre = v; 97 } 98 void cut(int u, int v){ 99 Make_rt(u); 100 Access(v); 101 Splay(v); 102 tr[lch(v)].pre = 0; 103 tr[lch(v)].rt = 1; 104 tr[v].pre = 0; 105 lch(v) = 0; 106 } 107 bool judge(int u, int v){ 108 while(tr[u].pre) u = tr[u].pre; 109 while(tr[v].pre) v = tr[v].pre; 110 return u == v; 111 } 112 int n, m, u, v; 113 int pre[N]; 114 struct Edge{ 115 int u, v, a, b; 116 bool operator < (const Edge & x) const { 117 if(a == x.a) return b < x.b; 118 return a < x.a; 119 } 120 }e[N]; 121 int Find(int x){ 122 if(x == pre[x]) return x; 123 return pre[x] = Find(pre[x]); 124 } 125 int main(){ 126 scanf("%d%d", &n, &m); 127 for(int i = 1; i <= n+m; i++) 128 tr[i].init(), pre[i] = i; 129 for(int i = 1; i <= m; i++) 130 scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b); 131 sort(e+1, e+1+m); 132 int ans = inf; 133 for(int i = 1; i <= m; i++){ 134 tr[i+n].val = tr[i+n].mx = e[i].b; 135 int u = e[i].u, v = e[i].v; 136 if(Find(u) != Find(v)){ 137 link(u, i+n); 138 link(v, i+n); 139 pre[Find(u)] = pre[Find(v)] = i+n; 140 } 141 else { 142 Make_rt(u); 143 Access(v); 144 Splay(v); 145 int k = tr[v].id; 146 if(tr[v].mx > e[i].b){ 147 cut(u, k); cut(v, k); 148 link(u, i+n); 149 link(v, i+n); 150 } 151 } 152 if(Find(1) == Find(n)){ 153 Make_rt(1); 154 Access(n); 155 Splay(n); 156 ans = min(ans, e[i].a + tr[n].mx); 157 } 158 } 159 if(ans == inf) ans = -1; 160 printf("%d\n", ans); 161 return 0; 162 }