洛谷 2387 NOI2014魔法森林 LCT

【题解】

  我们先把边按照$a$值从小到大排序,并按照这个顺序加边。

  如果当前要加入的边连接的两点$u$与$v$已经是连通的,那么直接加入这条边就会出现环。这时我们需要删除这个环中$b$值最大的边。因此我们需要维护区间最大值,以及最大值的位置。

  如果当前$1$与$n$已经连通,就更新$ans$,当前从$1~n$的代价是$ai+val[querymax(1,n)]$;

  为了方便处理,我们可以把边开成点,即加边的时候多开一个表示这条边的点,在上面记录边权等信息。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define N (500010)
  4 #define inf (2e9)
  5 #define ls (c[u][0])
  6 #define rs (c[u][1])
  7 using namespace std;
  8 int n,m,ans;
  9 inline int read(){
 10     int k=0,f=1; char c=getchar();
 11     while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
 12     while(‘0‘<=c&&c<=‘9‘)k=k*10+c-‘0‘,c=getchar();
 13     return k*f;
 14 }
 15 struct edge{int u,v,a,b;}e[N];
 16 struct Link_cut_tree{
 17     int top,c[N][2],fa[N],rev[N],q[N],maxpos[N],val[N];
 18     inline void pushdown(int u){
 19         if(rev[u]) rev[ls]^=1,rev[rs]^=1,rev[u]^=1,swap(ls,rs);
 20     }
 21     inline void pushup(int u){
 22         maxpos[u]=u;
 23         if(val[maxpos[ls]]>val[maxpos[u]]) maxpos[u]=maxpos[ls];
 24         if(val[maxpos[rs]]>val[maxpos[u]]) maxpos[u]=maxpos[rs];
 25     }
 26     inline bool isroot(int u){
 27         return c[fa[u]][0]!=u&&c[fa[u]][1]!=u;
 28     }
 29     inline bool which(int u){
 30         return c[fa[u]][1]==u;
 31     }
 32     void rotate(int u){
 33         int f=fa[u],gf=fa[f],wh=which(u);
 34         if(!isroot(f)) c[gf][which(f)]=u;
 35         fa[u]=gf; fa[f]=u; fa[c[u][wh^1]]=f;
 36         c[f][wh]=c[u][wh^1]; c[u][wh^1]=f;
 37         pushup(f); pushup(u);
 38     }
 39     void splay(int u){
 40         q[top=1]=u;
 41         for(int i=u;!isroot(i);i=fa[i]) q[++top]=fa[i];
 42         for(int i=top;i;i--) pushdown(q[i]);
 43         while(!isroot(u)){
 44             if(!isroot(fa[u])) rotate(which(fa[u])==which(u)?fa[u]:u);
 45             rotate(u);
 46         }
 47         pushup(u);
 48     }
 49     void access(int u){
 50         for(int son=0;u;son=u,u=fa[u]) splay(u),c[u][1]=son,pushup(u);
 51     }
 52     void makeroot(int u){
 53         access(u); splay(u); rev[u]^=1;
 54     }
 55     int find(int u){
 56         access(u); splay(u);
 57         while(ls) u=ls;
 58         return u;
 59     }
 60     void split(int x,int y){
 61         makeroot(x); access(y); splay(y);
 62     }
 63     void cut(int x,int y){
 64         split(x,y);
 65         c[y][0]=fa[x]=0;
 66         pushup(y);
 67     }
 68     void link(int x,int y){
 69         makeroot(x); fa[x]=y;
 70     }
 71     int query(int x,int y){
 72         makeroot(x); access(y); splay(y);
 73         return maxpos[y];
 74     }
 75 }t;
 76 bool cmp(edge x,edge y){
 77     return x.a<y.a;
 78 }
 79 int main(){
 80     ans=inf;
 81     n=read(); m=read();
 82     for(int i=1;i<=m;i++)
 83         e[i].u=read(),e[i].v=read(),e[i].a=read(),e[i].b=read();
 84     sort(e+1,e+m+1,cmp);
 85     for(int i=1;i<=m;i++){
 86         int u=e[i].u,v=e[i].v,a=e[i].a,b=e[i].b;
 87         if(t.find(u)==t.find(v)){
 88             int pos=t.query(u,v);
 89             if(t.val[pos]>b){
 90                 t.cut(pos,e[pos-n].u);
 91                 t.cut(pos,e[pos-n].v);
 92             }
 93             else{
 94                 if(t.find(1)==t.find(n)) ans=min(ans,a+t.val[t.query(1,n)]);
 95                 continue;
 96             }
 97         }
 98         t.val[n+i]=b; t.maxpos[n+i]=n+i;
 99         t.link(u,n+i); t.link(v,n+i);
100         if(t.find(1)==t.find(n)) ans=min(ans,a+t.val[t.query(1,n)]);
101     }
102     if(ans==inf) puts("-1");
103     else printf("%d\n",ans);
104     return 0;
105 }

原文地址:https://www.cnblogs.com/DriverLao/p/8277550.html

时间: 2024-10-07 23:58:20

洛谷 2387 NOI2014魔法森林 LCT的相关文章

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

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

【BZOJ3669】[Noi2014]魔法森林 LCT

[BZOJ3669][Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵.小E可以借助它们的力量,达到自

bzoj3669: [Noi2014]魔法森林 lct

记得去年模拟赛的时候好像YY出二分答案枚举a,b的暴力,过了55欸 然后看正解,为了将两维变成一维,将a排序,模拟Kruskal的加边过程,同时维护1到n的最大值,加入一条边e(u,v,a,b)时有以下两种情况: 1) 若u,v已连通,则找出u->v上最大的b',若b<b',则替换之,同时更新答案,注意e一定经过1->n,因为去掉b'所在边时1,n一定不连通,若加上e后1,n连通,则必经过e,由于a是有序的,所以a是路径上最大的a,用a+MAX_b[1->n]更新答案即可. 2)否

【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

Vijos1865 NOI2014 魔法森林 LCT维护生成树

基本思路: 首先按照weightA升序排序,然后依次在图中加边,并维护起点到终点路径上weightB的最大值 如果加边过程中生成了环,则删除环中weightB最大的边 由于是无向图,点之间没有拓扑序,所以在建立LCT模型时,可以将原图的边也视为点,这样就转化成了维护路径上的点权最大值(Orz Hzwer) 点的连通性可以用并查集维护 AC code:(其实Splay双旋一次时只需要进行3次update,而代码中舍弃了这个优化) 1 #include <cstdio> 2 #include &l

【洛谷P2387】魔法森林

这个题就是先按照a从小到大排序,然后lct尽可能维护b值最小,在这个过程中寻找答案 #include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int x,y,a,b; struct in { int f,l,a,b; }ter[100010]; int son[200020][2],p

bzoj3669 [Noi2014]魔法森林——LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 第一道LCT! 主要是看这个博客理解学LCT板子:https://blog.csdn.net/yxuanwkeith/article/details/50991326 关于这道题,又看了看这个博客:https://blog.csdn.net/clove_unique/article/details/51317842 然后努力抄写了半天,成功AC! 代码中 //// 的地方是我还有点不

NOI2014 魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 106  Solved: 62[Submit][Status] Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.

1685. [NOI2014]魔法森林

1685. [NOI2014]魔法森林 动态SPFA 求两个数的 最优值 我们可以限定一个值 求另一个的最优值 这个题 我们可以 对a排序 每次动态加边 就省去了 每次SPFA的dis和vis数组的清空 cogs上毫无压力 但是在UOJ上有一个额外的测试点 好像专门卡SPFA 毕竟这个正解应该是LCT嘛 1 #include <cctype> 2 #include <cstdio> 3 #include <algorithm> 4 #define max(a,b) a&