NOI 2014 魔法森林(BZOJ 3669) 题解

对边按a权值排序,按b权值建LCT,按排序后的顺序依次加边。如果加边后形成环则删除环上最大的边。如果起点终点联通则更新答案。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define rep(i,n) for(int i=0;i<n;++i)
  4 const int MAXN=50000+5;
  5 const int MAXM=100000+5;
  6 const int INF=~0U>>1;
  7 struct Node{
  8     int mxv,v;
  9     Node* p,*ch[2],*mx;
 10     int e;
 11     bool rev;
 12     Node() {mxv=v=-1;}
 13     inline bool d() const
 14     {
 15         return p->ch[1]==this;
 16     }
 17     inline void revIt()
 18     {
 19         rev^=1;
 20         std::swap(ch[0],ch[1]);
 21     }
 22     inline void setc(Node* t,bool d)
 23     {
 24         ch[d]=t;
 25         t->p=this;
 26     }
 27     inline void maintain()
 28     {
 29         mxv=v;mx=this;
 30         rep(i,2) if(ch[i]->mxv>mxv)
 31         {
 32             mxv=ch[i]->mxv;
 33             mx=ch[i]->mx;
 34         }
 35     }
 36     inline bool isRoot() const;
 37     inline void pushdown();
 38 }Tnull,*null=&Tnull;
 39 inline bool Node::isRoot() const
 40 {
 41     return p==null || (p->ch[0]!=this && p->ch[1]!=this);
 42 }
 43 inline void Node::pushdown()
 44 {
 45     if(rev)
 46     {
 47         rev=0;
 48         rep(i,2) if(ch[i]!=null)
 49             ch[i]->revIt();
 50     }
 51 }
 52 inline void rot(Node* t)
 53 {
 54     Node* p=t->p;
 55     p->pushdown();t->pushdown();
 56     bool d=t->d();
 57     if(!p->isRoot()) p->p->setc(t,p->d());
 58     else t->p=p->p;
 59     p->setc(t->ch[d^1],d);
 60     t->setc(p,d^1);
 61     p->maintain();
 62 }
 63 inline void splay(Node* t)
 64 {
 65     t->pushdown();
 66     while(!t->isRoot())
 67     {
 68         if(t->p->isRoot()) rot(t);
 69         else t->d()==t->p->d()?(rot(t->p),rot(t)):(rot(t),rot(t));
 70     }
 71     t->maintain();
 72 }
 73 inline void access(Node* u)
 74 {
 75     for(Node* v=null;u!=null;v=u,u=u->p)
 76     {
 77         splay(u);
 78         u->setc(v,1);
 79         u->maintain();
 80     }
 81 }
 82 inline void makeRoot(Node* u)
 83 {
 84     access(u);
 85     splay(u);
 86     u->revIt();
 87 }
 88 inline void link(Node* u,Node* v)
 89 {
 90     makeRoot(u);u->p=v;
 91 }
 92 inline void cut(Node* u,Node* v)
 93 {
 94     makeRoot(u);
 95     access(v);
 96     splay(v);
 97     if(v->ch[0]==u)
 98     {
 99         v->setc(null,0);
100         v->maintain();
101         u->p=null;
102     }
103 }
104 inline Node* find(Node* u)
105 {
106     for(;u->p!=null;u=u->p);
107     return u;
108 }
109 inline void change(Node* u,int v)
110 {
111     access(u);
112     splay(u);
113     u->v=v;
114     u->maintain();
115 }
116 inline void getMax(Node* u,Node* v,int& maxv,int& e,Node*& mx)
117 {
118     makeRoot(u);
119     access(v);
120     splay(v);
121     maxv=v->mxv;
122     mx=v->mx;
123     e=v->mx->e;
124 }
125 inline int query(Node* u,Node* v)
126 {
127     makeRoot(u);
128     access(v);
129     splay(v);
130     return v->mxv;
131 }
132 Node mem[MAXN+MAXM],*C=mem;
133 inline Node* newNode(int v,int e)
134 {
135     C->p=C->ch[0]=C->ch[1]=null;
136     C->rev=0;
137     C->v=C->mxv=v;
138     C->mx=C;
139     C->e=e;
140     return C++;
141 }
142 struct Edge{
143     int u,v,a,b;
144     Edge() {}
145     Edge(int u,int v,int a,int b):u(u),v(v),a(a),b(b) {}
146     bool operator<(const Edge& rhs) const
147     {
148         return a<rhs.a;
149     }
150 }edges[MAXM];
151 Node* pt[MAXN+MAXM];
152 int n,m;
153 int ans=INF;
154 int main()
155 {
156     //freopen("1.in","r",stdin);
157     scanf("%d%d",&n,&m);
158     rep(i,m)
159     {
160         int u,v,a,b;
161         scanf("%d%d%d%d",&u,&v,&a,&b);
162         edges[i]=Edge(u,v,a,b);
163     }
164     std::sort(edges,edges+m);
165     rep(i,n) pt[i+1]=newNode(0,-1);
166     rep(i,m) pt[i+n+1]=newNode(edges[i].b,i);
167     rep(i,m)
168     {
169         Edge e=edges[i];
170         if(find(pt[e.u])!=find(pt[e.v])) link(pt[e.u],pt[i+n+1]),link(pt[e.v],pt[i+n+1]);
171         else
172         {
173             int en,maxv;
174             Node* mx;
175             getMax(pt[e.u],pt[e.v],maxv,en,mx);
176             if(maxv>e.b)
177             {
178                 cut(pt[edges[en].u],pt[en+n+1]);
179                 cut(pt[edges[en].v],pt[en+n+1]);
180                 link(pt[e.u],pt[i+n+1]);
181                 link(pt[e.v],pt[i+n+1]);
182             }
183         }
184         if(find(pt[1])==find(pt[n])) ans=std::min(ans,query(pt[1],pt[n])+edges[i].a);
185     }
186     printf("%d\n",ans==INF?-1:ans);
187     return 0;
188 }

时间: 2024-11-05 08:47:50

NOI 2014 魔法森林(BZOJ 3669) 题解的相关文章

[BZOJ 3669][NOI 2014]魔法森林(Link-Cut Tree)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3669 记得四个月之前的NOI同步赛,我还只会玩脚丫子.... 记得我当时看到这个题整个人就吓傻了,完全不知道怎么做,然后NOI同步赛就这样爆零了... 如今我学了LCT这个神器,再看这个题,感觉不再那么难了. 其实这个题的标准解法是SPFA,改得完全认不出来的SPFA. orz太神了,完全没见识过这么神犇的SPFA,NOIP 2014考了SPFA,NOI 2014也考了SPFA,

【BZOJ】3669: [Noi2014]魔法森林(lct)

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 首先看到题目应该可以得到我们要最小化 min{ max{a(u, v)} + max{b(u, v)} } 两个变量不好做...那么我们约束一个a 即按a从小到大排序,依次加边. 发现当有环出现时,去掉的是环中b最大的边. 证明:因为a是从小到大排序,因此此时答案为 a+max{b(u, v)},显然b越小越好. 然后需要link和cut操作... 脑洞开到这里开不动了...........

bzoj 3669: [Noi2014]魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的

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

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

bzoj 3669: [Noi2014]魔法森林 动态树

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

【BZOJ】【3669】【NOI2014】魔法森林

LCT动态维护MST LCT动态维护MST 我们可以枚举a,然后找从1到n的一条路径使得:这条路径上的b的最大值最小.这个路径肯定在MST上……所以枚举一遍所有的边,动态维护一个关于b值的MST即可. 调了半天没出解的原因: rotate写错了……l=c[y][1]==x 我写成了 l=c[z][1]==y sigh…… 1 /************************************************************** 2 Problem: 3669 3 User

【bzoj 3669】[Noi2014]魔法森林

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

[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 的路径中边权最大

BZOJ3669:[NOI2014]魔法森林——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 https://www.luogu.org/problemnew/show/P2387 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m.初始时小 E 同学在 1 号节点,隐士则住在 n 号节点.小 E 需要通过这一片魔法森林,才能够拜访到隐士. 魔