bzoj 3669 lct维护最小生成树

大概题意:给一个无向图,有a,b两种边权,找一条从1到n的路径,使得max(a[i])+max(b[i])最小a[i],b[i]表示该路径上的边的对应权。

如果用类似最短路的DP来做,显然每个点的状态就必须是一个集合,保存的是一个下凸的点集,表示到达这个点的最小的a,b,这样肯定会挂,但该该种做法已经无法再优化或减少状态了。

考虑枚举其中一个权值b0,然后只考虑所有b权值小于等于b0的边,然后变成简单的问题,因为这个b0不满足二分三分之类的性质,所以肯定不能每次重建图,跑DP,最终的做法是从小到大枚举一维权值,然后不断地加边进入边,维护一个当前图的最小生成树(用LCT实现)。

收获:

  1、如果一个东西不满足二分三分,可以尝试从动态的角度,暴力枚举,用数据结构保证复杂度。

  1 /**************************************************************
  2     Problem: 3669
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:6680 ms
  7     Memory:12536 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <algorithm>
 12 #define oo 0x3f3f3f3f
 13 #define N 200010
 14 using namespace std;
 15
 16 struct Edge {
 17     int u, v, a, b;
 18     Edge(){}
 19     Edge( int u, int v, int a, int b ):u(u),v(v),a(a),b(b){}
 20     bool operator<( const Edge &e ) const { return b<e.b; }
 21 };
 22 struct LCT {
 23     int son[N][2], pre[N], pnt[N], rtg[N], ntot;
 24     int trash[N], stot;
 25     int val[N], vmx[N], vnd[N], uu[N], vv[N];
 26
 27     inline int newnode( int p, int va, int u, int v ) {
 28         int nd = stot ? trash[stot--] : ++ntot;
 29         pre[nd] = p;
 30         son[nd][0] = son[nd][1] = pnt[nd] = 0;
 31         rtg[nd] = 0;
 32         val[nd] = vmx[nd] = va;
 33         vnd[nd] = nd;
 34         uu[nd] = u;
 35         vv[nd] = v;
 36         return nd;
 37     }
 38     void update( int nd ) {
 39         vmx[nd] = val[nd];
 40         vmx[nd] = max( vmx[nd], vmx[son[nd][0]] );
 41         vmx[nd] = max( vmx[nd], vmx[son[nd][1]] );
 42         if( vmx[nd]==val[nd] )
 43             vnd[nd] = nd;
 44         else if( vmx[nd]==vmx[son[nd][0]] )
 45             vnd[nd] = vnd[son[nd][0]];
 46         else
 47             vnd[nd] = vnd[son[nd][1]];
 48     }
 49     void rotate( int nd, int d ) {
 50         int p = pre[nd];
 51         int s = son[nd][!d];
 52         int ss = son[s][d];
 53
 54         son[nd][!d] = ss;
 55         son[s][d] = nd;
 56         if( p ) son[p][ nd==son[p][1] ] = s;
 57         else pnt[s]=pnt[nd], pnt[nd]=0;
 58
 59         pre[nd] = s;
 60         pre[s] = p;
 61         if( ss ) pre[ss] = nd;
 62
 63         update(nd);
 64         update(s);
 65     }
 66     void bigp( int nd ) {
 67         if( pre[nd] ) bigp(pre[nd]);
 68         if( rtg[nd] ) {
 69             rtg[son[nd][0]] ^= 1;
 70             rtg[son[nd][1]] ^= 1;
 71             swap( son[nd][0], son[nd][1] );
 72             rtg[nd] = 0;
 73         }
 74     }
 75     void splay( int nd, int top=0 ) {
 76         bigp(nd);
 77         while( pre[nd]!=top ) {
 78             int p=pre[nd];
 79             int nl=nd==son[p][0];
 80             if( pre[p]==top ) {
 81                 rotate( p, nl );
 82             } else {
 83                 int pp=pre[p];
 84                 int pl=p==son[pp][0];
 85                 if( nl==pl ) {
 86                     rotate( pp, pl );
 87                     rotate( p, nl );
 88                 } else {
 89                     rotate( p, nl );
 90                     rotate( pp, pl );
 91                 }
 92             }
 93         }
 94     }
 95     void access( int nd ) {
 96         int u = nd;
 97         int v = 0;
 98         while( u ) {
 99             splay( u );
100             int s=son[u][1];
101             if( s ) {
102                 pre[s] = 0;
103                 pnt[s] = u;
104             }
105             son[u][1] = 0;
106             if( v ) {
107                 pre[v] = u;
108                 pnt[v] = 0;
109             }
110             son[u][1] = v;
111             update(u);
112             v = u;
113             u = pnt[u];
114         }
115         splay( nd );
116     }
117     void makeroot( int u ) {
118         access(u);
119         rtg[u] ^= 1;
120     }
121     void init( int n ) {
122         ntot = n;
123         val[0] = -1;
124     }
125     int findroot( int u ) {
126         while( pre[u] ) u=pre[u];
127         while( pnt[u] ) {
128             u=pnt[u];
129             while( pre[u] ) u=pre[u];
130         }
131         return u;
132     }
133     bool sameroot( int u, int v ) {
134         return findroot(u)==findroot(v);
135     }
136     int query( int s, int t ) {
137         makeroot(s);
138         access(t);
139         return vmx[t];
140     }
141     void addedge( int u, int v, int w ) {
142 //      fprintf( stderr, "addedge( %d %d %d )\n", u, v, w );
143         if( sameroot(u,v) ) {
144             if( query(u,v)<=w ) return;
145             makeroot(u);
146             access(v);
147             int nd = vnd[v];
148             int l = uu[nd];
149             int r = vv[nd];
150             makeroot(l);
151             access(r);
152             bigp(l);
153             int e = son[r][0];
154             while( son[e][1] ) e=son[e][1];
155             splay(e);
156             pre[l] = pre[r] = 0;
157             trash[++stot] = e;
158         }
159         makeroot(u);
160         makeroot(v);
161         int nd = newnode(0,w,u,v);
162         pnt[u] = nd;
163         pnt[v] = nd;
164     }
165     void print() {
166         fprintf( stderr, "\n" );
167         for( int i=1; i<=ntot; i++ ) {
168             int nd = i;
169             for( int j=1; j<=stot; j++ )
170                 if( i==trash[j] )
171                     goto Next;
172             fprintf( stderr, "%d pnt=%d pre=%d ls=%d rs=%d rtg=%d val=%d vmx=%d vnd=%d uu=%d vv=%d\n",
173                     nd, pnt[nd], pre[nd], son[nd][0], son[nd][1], rtg[nd], val[nd], vmx[nd], vnd[nd], uu[nd], vv[nd] );
174 Next:;
175         }
176     }
177 }T;
178
179 int n, m;
180 Edge edge[N];
181
182 int main() {
183     scanf( "%d%d", &n, &m );
184     for( int i=1,u,v,a,b; i<=m; i++ ) {
185         scanf( "%d%d%d%d", &u, &v, &a, &b );
186         edge[i] = Edge(u,v,a,b);
187     }
188     sort( edge+1, edge+1+m );
189     T.init(n);
190     int ans = oo;
191     for( int i=1,j; i<=m; i=j ) {
192         for( j=i; j<=m; j++ ) {
193             if( edge[j].b==edge[i].b )
194                 T.addedge(edge[j].u,edge[j].v,edge[j].a);
195             else break;
196         }
197         if( !T.sameroot(1,n) ) continue;
198         ans = min( ans, edge[i].b+T.query(1,n) );
199     }
200     printf( "%d\n", ans==oo ? -1 : ans );
201 }

时间: 2024-12-28 23:25:01

bzoj 3669 lct维护最小生成树的相关文章

【BZOJ 3669】 [Noi2014]魔法森林 LCT维护动态最小生成树

这道题看题意是在求一个二维最小瓶颈路,唯一可行方案就是枚举一维在这一维满足的条件下使另一维最小,那么我们就把第一维排序利用A小的边在A大的情况下仍成立来动态加边维护最小生成树. #include <cstdio> #include <algorithm> namespace Pre{ inline void read(int &sum){ register char ch=getchar(); for(sum=0;ch<'0'||ch>'9';ch=getcha

3669 [Noi2014]魔法森林(LCT,最小生成树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3669 [题意] 给定一个无向图,求1-n的路径中最小的max{ai}+max{bi} [思路] 将边按照a排序.LCT维护关于b的最小生成树. 顺序枚举每条边u,v,如果u,v已经连接则比较u,v路径上的最大边与新边,否则直接相连. 如果1与n连通,则用e.a+max{e.b}更新ans.直观地看,最小生成树上的max{e.b}是1..i条边加入后能够得到的最小b. _max的初值赋

[BZOJ 3669] [Noi2014] 魔法森林 【LCT】

题目链接:BZOJ - 3669 题目分析 如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小. 其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径.只能使用 Ai <= x 的边. 那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了. 如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大

BZOJ 3669: [Noi2014]魔法森林( LCT )

排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) -------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1

洛谷P4172 [WC2006]水管局长 (LCT,最小生成树)

思路分析 在一个图中,要求路径上最大边边权最小,就不难想到最小生成树.而题目中有删边的操作,那肯定是要动态维护啦.直接上LCT维护边权最小值(可以参考一下蒟蒻的Blog) 这时候令人头疼的问题又冒出来了......删掉一条边以后,又不好从树断开后的两边选出最小的边在连上.这是根本维护不了的. 于是蒟蒻又get到了一个新套路--顺序解决不了的问题,可以离线询问,反过来处理.原来的删边变成了加边,就很方便了.直接split找出环上的最大边,当前要加的边比它小就替换掉. 一个做法的问题:在反过来初始化

bzoj2555 LCT维护后缀自动机

通过用LCT维护parent树来实现后缀自动机的在线操作. 注意right值初始化为0,然后加新结点的时候只要将np的right值设为1,而不需要改变nq的right值,因为nq是内部的结点,np才是外层的结点. 思路很简单,代码真长,调了挺久....不过写起来还算清晰... #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using

【bzoj4530】[Bjoi2014]大融合 LCT维护子树信息

题目描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量. 例如,在上图中,现在一共有了5条边.其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8). 现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问. 输入 第一行包含两个整数N,Q,表示星球的

【BZOJ3510】首都 LCT维护子树信息+启发式合并

[BZOJ3510]首都 Description 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失,而B国的国土也将归A国管辖.A国国王为了加强统治,会在A国和B国之间修建一条公路,即选择原A国的某个城市和B国某个城市,修建一条连接这两座城市的公路. 同样为了便于统治自己的国家,国家的首都会选在某个使得其他城市到它距离之和最小的城市,这里的距离是指需要经过公

bzoj 2631 LCT

/************************************************************** Problem: 2631 User: wangyucheng Language: C++ Result: Accepted Time:18408 ms Memory:9252 kb ****************************************************************/ #include<iostream> #include