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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vijos1865 NOI2014 魔法森林 LCT维护生成树相关的知识,希望对你有一定的参考价值。

基本思路:

首先按照weightA升序排序,然后依次在图中加边,并维护起点到终点路径上weightB的最大值

如果加边过程中生成了环,则删除环中weightB最大的边

 由于是无向图,点之间没有拓扑序,所以在建立LCT模型时,可以将原图的边也视为点,这样就转化成了维护路径上的点权最大值(Orz Hzwer)

点的连通性可以用并查集维护

AC code:(其实Splay双旋一次时只需要进行3次update,而代码中舍弃了这个优化)

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 const int maxN=50005;
  6 const int maxM=100005;
  7 const int inf=0x3f3f3f3f;
  8 
  9 struct Edge
 10 {
 11     int u,v;
 12     int weightA,weightB;
 13 
 14     void assign(int _u,int _v,int _wa,int _wb)
 15     {
 16         u=_u; v=_v; weightA=_wa; weightB=_wb;
 17     }
 18     bool operator < (const Edge& other) const
 19     {
 20         return this->weightA < other.weightA;
 21     }
 22 };
 23 
 24 Edge elist[maxM];
 25 
 26 inline int getMaxIdx(int u,int v)
 27 {
 28     return elist[u].weightB > elist[v].weightB ? u : v;
 29 } //Let elist[0].weightB = -inf
 30 
 31 struct Splay
 32 {
 33     int idx;
 34     int maxIdx;
 35     bool rev;
 36     Splay* child[2];
 37     Splay* parent;
 38 
 39     void init(int id)
 40     {
 41         idx=maxIdx=id;
 42         rev=false;
 43         child[0]=child[1]=parent=0;
 44     }
 45     bool isRoot()
 46     {
 47         if(!parent) return true;
 48         else return this!=parent->child[0] && this!=parent->child[1];
 49     }
 50     void update()
 51     {
 52         maxIdx=idx;
 53         if(child[0]) maxIdx=getMaxIdx(this->maxIdx,child[0]->maxIdx);
 54         if(child[1]) maxIdx=getMaxIdx(this->maxIdx,child[1]->maxIdx);
 55     }
 56     void reverse()
 57     {
 58         if(!isRoot()) parent->reverse();
 59         if(rev) {
 60             std::swap(child[0],child[1]);
 61             if(child[0]) child[0]->rev ^= 1;
 62             if(child[1]) child[1]->rev ^= 1;
 63             this->rev=0;
 64         }
 65     }
 66     void setRev() { rev^=1; }
 67     void rotate(int dir)
 68     {
 69         Splay* temp=parent;
 70         this->parent=temp->parent;
 71         if(parent)
 72         {
 73             if(temp==parent->child[0]) parent->child[0]=this;
 74             else if(temp==parent->child[1]) parent->child[1]=this;
 75         }
 76         temp->child[dir^1]=this->child[dir];
 77         if(child[dir]) child[dir]->parent=temp;
 78         child[dir]=temp;
 79         temp->parent=this;
 80         temp->update();
 81         this->update();
 82     }
 83     void splay()
 84     {
 85         reverse();
 86         while(!isRoot())
 87         {
 88             int st(0);
 89             if(this==parent->child[0]) st|=1;
 90             else st|=2;
 91             if(!parent->isRoot())
 92             {
 93                 if(parent==parent->parent->child[0]) st|=4;
 94                 else st|=8;
 95             }
 96             switch(st)
 97             {
 98             case 1: rotate(1); break;
 99             case 2: rotate(0); break;
100             case 5: parent->rotate(1); this->rotate(1); break;
101             case 6: rotate(0); rotate(1); break;
102             case 9: rotate(1); rotate(0); break;
103             case 10:parent->rotate(0); this->rotate(0); break;
104             }
105         }
106     }
107 };
108 
109 Splay node[maxN+maxM];
110 
111 int access(int idx)
112 {
113     Splay *cur,*last;
114     for(cur=node+idx,last=0;cur;last=cur,cur=cur->parent)
115     {
116         cur->splay();
117         cur->child[1]=last;
118         cur->update();
119     }
120     return last->maxIdx;
121 }
122 inline void setAsRoot(int idx)
123 {
124     access(idx);
125     node[idx].splay();
126     node[idx].setRev();
127 }
128 inline void link(int u,int v)
129 {
130     setAsRoot(u);
131     node[u].parent=node+v;
132 }
133 inline void cut(int u,int v)
134 {
135     setAsRoot(u);
136     access(v);
137     node[v].splay();
138     node[v].child[0]=node[u].parent=0;
139     node[v].update();
140 }
141 inline int query(int u,int v)
142 {
143     setAsRoot(u);
144     return access(v);
145 }
146 
147 int N,M;
148 int center[maxN];
149 
150 int getCenter(int idx)
151 {
152     return center[idx]==idx ? idx :
153         center[idx]=getCenter(center[idx]);
154 }
155 void input()
156 {
157     scanf("%d%d",&N,&M);
158     int u,v,wa,wb;
159     for(int i=1;i<=M;i++)
160     {
161         scanf("%d%d%d%d",&u,&v,&wa,&wb);
162         if(u==v) { --i; --M; }
163         else elist[i].assign(u,v,wa,wb);
164     }
165 }
166 void init()
167 {
168     elist[0].weightB=-inf;
169     std::sort(elist+1,elist+M+1);
170     for(int i=1;i<=M;i++) node[i].init(i);
171     for(int i=1;i<=N;i++) node[i+M].init(0);
172     for(int i=1;i<=N;i++) center[i]=i;
173 }
174 void addEdge(int e)
175 {
176     int& u=elist[e].u;
177     int& v=elist[e].v;
178     int cu=getCenter(u);
179     int cv=getCenter(v);
180     if(cu!=cv)
181     {
182         link(e,M+u);
183         link(e,M+v);
184         center[cu]=cv;
185     }
186     else {
187         int mx=query(M+u,M+v);
188         if(elist[e].weightB < elist[mx].weightB)
189         {
190             cut(mx,M+elist[mx].u);
191             cut(mx,M+elist[mx].v);
192             link(e,M+u);
193             link(e,M+v);
194         }
195     }
196 }
197 int solve()
198 {
199     int ans(inf);
200     init();
201     for(int i=1;i<=M;i++)
202     {
203         addEdge(i);
204         if(getCenter(1)==getCenter(N))
205         {
206             int mx=query(M+1,M+N);
207             ans=std::min(ans,elist[i].weightA+elist[mx].weightB);
208         }
209     }
210     return (ans==inf)?-1:ans;
211 }
212 
213 int main()
214 {
215     input();
216     printf("%d\\n",solve());
217     return 0;
218 }

 

——————分割线——————

这道题我Debug了2天共计5h,最后偶然间查明了死因居然是——

技术分享
 1 #ifdef WRONG_SPLAY_CODE
 2 void Splay::splay()
 3 {
 4     reverse();
 5     while(!isRoot())
 6     {
 7         int st(0);
 8         if(this==parent->child[0]) st|=1;
 9         else st|=2;
10         if(!parent->isRoot())
11         {
12             if(parent==parent->parent->child[0]) st|=4;
13             else st|=8;
14         }
15         switch(st)
16         {
17         case 1: rotate(1); break;
18         case 2: rotate(0); break;
19         case 5: parent->rotate(1); this->rotate(1); break;
20         case 6: rotate(1); rotate(0); break;
21         case 9: rotate(0); rotate(1); break;
22         case 10:parent->rotate(0); this->rotate(0); break;
23         }
24     }
25 }
26 #endif
I FELL COLLAPSED BECAUSE OF THIS

不过在Debug期间,参考了不少其他神犇的AC代码,也学到了各种姿势,算是因祸得福吧……

以上是关于Vijos1865 NOI2014 魔法森林 LCT维护生成树的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3669: [Noi2014]魔法森林

bzoj 3669 [Noi2014]魔法森林

BZOJ3669: [Noi2014]魔法森林

bzoj3669: [Noi2014]魔法森林

[BZOJ3669][Noi2014]魔法森林

BZOJ3669[Noi2014]魔法森林 LCT