删边
昨天$asuldb$问了我一道题,那道题很神奇.要动态的维护一棵最小生成树,有时还需要删掉一些边,这就出现了一系列的问题.因为总的边数非常多,删边的时候如果使用所谓的"惰性删除",也就是打一个标记表示这条边被删掉了,就会极大地降低效率,因为每次在生成树上找环时会访问很多的无效边,所以必须动态地删除才可以,可是...网上似乎没有这方面的资料?
图论中有删除操作的不多,比较典型的就是欧拉回路中走过去要删掉,但是那种做法似乎不是很适合这道题...还有$LCT$
有一种最简单的方法:邻接矩阵存图,可以非常方便的实现$O(1)$删除,而且删反向边也极其简单.$g[x][y]=g[y][x]=0$.不过这种方法的局限性也是非常明显的,毕竟大多数图论题的图都是比较稀疏的,空间也开不下.
另一种比较$C++$的方法:采用$vector$存边,可以做到$O(N)$的删除,对于那道题来说就很够用了,因为每次在树上$dfs$也已经有了$O(N)$的复杂度.
但是!经过一节课的修改和试验,我发现了一种真正的$O(1)$删边方法,而且非常的简单,还可以删相应的反向边,这一点第二个做法似乎是做不到的.这种方法是基于链式前向星存图的,因为这种做法本质上就是$N$个链表来存边,而链表的删除就是常规操作了.注意如果当前的边是这个点发出的第一条边要特殊处理一下.所以这个为什么想了一节课...突然发现真的好简单啊...
关于反向边的删除:其实非常的简单,就是利用网络流的技巧,边集数组从$0$开始计数,那每条边相对应的反向边就是$iquad xorquad 1$.用一样的方法删掉就好啦.
刚刚想到的奇怪做法:依旧使用惰性删除,但是每加入$m$条边就将所有边删去进行重构,有点像替罪羊树的感觉(虽然我不会),可能也可以将复杂度保持在一个看得过去的复杂度上.
1 void add (int x,int y) 2 { 3 t[++h].too=y; 4 t[h].fro=x; 5 t[ firs[x] ].pre=h; 6 t[h].nex=firs[x]; 7 firs[x]=h; 8 } 9 10 void del (int id) 11 { 12 if(t[id].pre) 13 { 14 t[ t[id].pre ].nex=t[id].nex; 15 t[ t[id].nex ].pre=t[id].pre; 16 } 17 else 18 { 19 firs[ t[id].fro ]=t[id].nex; 20 t[ t[id].nex ].pre=0; 21 } 22 } 23 24 void print_ed() 25 { 26 printf(" "); 27 for (R i=1;i<=n;++i) 28 { 29 printf("%d->",i); 30 for (R j=firs[i];j;j=t[j].nex) 31 printf("%d ",t[j].too); 32 printf(" "); 33 } 34 printf(" "); 35 }
---shzr