(重发下我这篇原发于 2014-03-24 的网易博客,原博客被网易莫名禁掉了。。被迫手动搬家,忧伤)
动态仙人掌 III
但是我们想想,图中描述的情况说,从下面绕条道经过非树边到达需要打标的链,可能吗?
绿色和紫色圈住的地方是某条边的prevExMsg和某条边的nextExMsg。
……
能link、cut、查询最短路信息、对最短路进行整体操作。
于是我们当然可以一拍脑袋说道:水题!能查询难道不能打标记么?直接裸上啊。
但是随即就遇到了阻力……
需要注意的事情是,拓展信息可以是互相重叠的,也就是说,一段路径可能被多个结构维护。这是概述里面提到的禁忌。
但是是不是这样的话一定就不能打标记了呢?
我们不妨硬着头皮来试试。
程序读入了一个修改操作,然后对指定的路径进行打标,一定是这样的:
add(x, y) { if (findRoot(x) != findRoot(y)) return failed; makeRoot(x); access(y); splay(y); if (y->msg中记录的信息表明有多条最短路) return failed; 给以y为根的splay打上修改标记; return ok; }
那么如果现在想标记下传了,怎么办?
凉拌!(
我在这个地方被虐傻了……)
说从前有个最短路信息,它是由左边的一半和右边的一半合并而成的。(更准确地说是lc->msg, x->prevE, x->nextE, rc->msg),那么我们记录下取最小值的是哪条路,然后下传时打标记到对应的子树和边,不就行了。(如果不记得符号的意义可以看之前的日志)
说起来倒轻巧,这里面要讨论的条件的个数已经骇人听闻了。
我只会sb方法,用位运算压位的方式记录取最小值的来源。(你可以翻到后面我的代码里面lct_message::appendL、lct_message::appendR、lct_node::tag_down那里感受一下…… = =)
这样下传的话就不可避免地遇到一个问题:要是在链外的环上走,这标记打给谁?
于是我们一拍脑门道,当然,打在边的拓展信息上!
于是我们走上了一条不归路……
既然打在拓展信息上,就要保证access的时候能及时下传。但是一条链可能活在好多拓展信息中,而且还不全是它的父结构,这该怎么办?如下图是一个例子。
不可能!因为nextExMsg不会经过非树边。
也就是说,这种麻烦情况实际上只有一种类型:有条链在很偏远的地方的最顶端prevExMsg影响了其它链。而这两条链可能没有任何父子这类的关系。这在access的时候怎么能知道呢?
但是我们思考一下,情况何以至如此?
原因就在于我们的拓展信息并没有很明显的层次结构。如果我们能变成“拓展信息永远是向下拓展的”的话,打标也就容易了。
那么何为向下?显然,链之间是有上下关系的。我们将向叶子的方向定为向下。
这样我们发现,其实拓展信息一般来说都是向下的,如图:
这个显然是向下的。
画了几个图好像都是向下的。
这不得不引起我们的反思:对于lct来说,何为向下?
如果忽略一个结点往父亲走的情况,那么若一个结点与另一个结点相邻,但中间的边是虚边,那么就是向下!走虚边就是向下!
而我们的拓展信息永远是往“外”走,那么肯定一出门就是虚边,所以肯定是向下的。
于是我酣畅淋漓地写了一个。然后WA了。
为什么?
我们忽略了一条神奇的边的存在:链顶端的边。
于是也就忽略了一位让人肃然起敬的拓展信息:链顶端的边的prevExMsg。
它就是我们推理中的例外,因为它可以往实边走。
![](http://tb2.bdstatic.com/tb/editor/images/qpx_n/b36.gif?t=20140803)
没办法,那我们就不定义链顶端的边的prevExMsg,放弃优美的定义,这总该行了吧。
然后发现我们回到了最初的原点,我们无法access。
来我们再来看那张经典的图。我们不用
不定义链顶端的边的prevExMsg,能不能access?
困难是,我们不知道x4->prevE->prevExMsg。所以我们要想办法把它捡回来。
我们不妨再记录
x4->prevE->prevExFirstE。表示prevExMsg所代表的路径的第一条边,即往prev方向一往链外走碰到的第一条边。当然,即使有prevExMsg也可能没有prevExFirstE。比如一出家门就碰到非树边的情况。
1. 如果
没有prevExFirstE,那么我们也不需折腾那么许多,搞个长度为0的路径给prevExMsg就好了。
1. x4->prevE的环编号如果和x1->prevE的环编号相同,那么反正待会儿要置为空,无视。
2. x4->prevE的环编号如果和x1->nextE的环编号相同,那么从x1往x2的方向走的路径的信息中可以求出
x4->prevE->prevExMsg。把headMsg和headExMsg合并起来即可。
3. 如果上面两种情况都未发生,那么prevExFirstE肯定是条虚边。虚边?那么肯定是这样:
所以我们只需要找到
prevExFirstE所处的链,取出路径信息中的headMsg和headExMsg然后合并起来就得到prevExMsg了。
看起来这样做了之后就能顺利access。
但是我们怎么知道“
prevExFirstE所处的链
”?
简单,我们对于每条位于链的顶端的边,记录一个指针指向它所在的链的splay的根。
对于那棵splay来说,这条边是它的firstE,每次splay的时候注意把firstE的那个指针改成新的根结点就好了。
在access的时候也要小小地调整一下,不成问题。
那么现在终于有了清晰的层次结构,怎么打标呢?
显然最后打标的任务会落在拓展信息头上。但是我们发现我们其实记录了
prevExFirstE和next
ExFirstE,还知道它们所处的链的根结点。
那么直接打在根结点上就好了!
access的伪代码示例:
void access(x) { allFaTagDown(x); // 把x到根的所有标记下传。这里的根说的是树的总根。 for (p = x, q = NULL; p; q = p, p = p->fa) { p->splay(); // 这里的splay不执行标记下传 qFirstE = q ? q->msg.firstE : NULL; // 下面出现的*运算表示信息的合并 // path_message(e) 表示一条边e的原子信息 // 计算出qFirstE->prevExMsg if (getCirNum(qFirstE) != NULL) { if (getCirNum(qFirstE) != getCirNum(p->prevE)) { if (qFirstE->prevExFirstE != NULL) { pr = getCirNum(qFirstE) == getCirNum(p->nextE) ? p->rc : qFirstE->prevExFirstE->whoseFirstE; qFirstE->prevExMsg = path_message(qFirstE->prevExFirstE) * pr->msg.headMsg * pr->msg.headExMsg; } else qFirstE->prevExMsg.setEmpty(); } else { qFirstE->prevExMsg.setInvalid(); qFirstE->prevExFirstE = NULL; } } // 进行换边操作时拓展信息的调整 if (getCirNum(p->prevE) != NULL) { if (getCirNum(p->prevE) == getCirNum(qFirstE)) { p->prevE->nextEx_setInvalid(); qFirstE->prevEx_setInvalid(); } if (getCirNum(p->prevE) == getCirNum(p->nextE)) { p->prevE->nextExMsg = p->rc->msg.headExMsg * p->rc->msg.headMsg * path_message(p->nextE); p->prevE->nextExFirstE = p->nextE; // 可以不用管p->nextE->prevExMsg的值 p->nextE->prevExFirstE = p->prevE; } } // 更新一下相关的边是哪条链的第一条边并进行换边操作 if (p->rc != NULL) { if (p->rc->msg.firstE) p->rc->msg.firstE->whoseFirstE = p->rc; } p->nextE = qFirstE; p->rc = q; if (qFirstE != NULL) qFirstE->whoseFirstE = NULL; p->update(); } }
这样我们就顺利解决了动态仙人掌 III。
![](http://tb2.bdstatic.com/tb/editor/images/qpx_n/b37.gif?t=20140803)
这样做虽然比较繁琐但是仍然坚挺在O(n log n)。
(如果你不是很喜欢的话没关系!可以试试下一篇日志将要介绍的link-cut cactus)
有什么细节不清楚的可以看代码。(写了我1000行……分类讨论一口血喷出来……)
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <cassert> #include <climits> using namespace std; const int INF = INT_MAX; const int MaxN = 100000; inline int getint() { char c; while (c = getchar(), '0' > c || c > '9'); int res = c - '0'; while (c = getchar(), '0' <= c && c <= '9') res = res * 10 + c - '0'; return res; } template <class T> class BlockAllocator { private: static const int BlockL = 10000; union TItem { char rt[sizeof(T)]; TItem *next; }; TItem *pool, *tail; TItem *unused; public: BlockAllocator() { pool = NULL; unused = NULL; } T *allocate() { TItem *p; if (unused) { p = unused; unused = unused->next; } else { if (pool == NULL) pool = new TItem[BlockL], tail = pool; p = tail++; if (tail == pool + BlockL) pool = NULL; } return (T*)p; } void deallocate(T *pt) { TItem *p = (TItem*)pt; p->next = unused, unused = p; } }; struct edgeWeight; struct path_message; struct edge; struct lct_edge; struct lct_message; struct lct_node; struct edgeWeight { int wA, wB; edgeWeight(){} edgeWeight(const int &_wA, const int &_wB) : wA(_wA), wB(_wB){} friend inline bool operator==(const edgeWeight &lhs, const edgeWeight &rhs) { return lhs.wA == rhs.wA && lhs.wB == rhs.wB; } friend inline bool operator!=(const edgeWeight &lhs, const edgeWeight &rhs) { return lhs.wA != rhs.wA || lhs.wB != rhs.wB; } }; struct path_message { int minLA; int minWB; path_message(){} path_message(const int &_minLA, const int &_minWB) : minLA(_minLA), minWB(_minWB){} void setEmpty() { minLA = 0; minWB = INF; } void setInvalid() { minLA = -1; minWB = -1; } bool valid() const { return minLA != -1; } void wB_add(int delta) { if (minWB != INF) minWB += delta; } friend inline path_message operator+(const path_message &lhs, const path_message &rhs) { if (lhs.minLA < rhs.minLA) return lhs; else if (rhs.minLA < lhs.minLA) return rhs; else return path_message(lhs.minLA, -1); } friend inline path_message operator*(const path_message &lhs, const path_message &rhs) { return path_message(lhs.minLA + rhs.minLA, min(lhs.minWB, rhs.minWB)); } friend inline path_message operator*(const edgeWeight &lhs, const path_message &rhs) { return path_message(lhs.wA + rhs.minLA, min(lhs.wB, rhs.minWB)); } friend inline path_message operator*(const path_message &lhs, const edgeWeight &rhs) { return path_message(lhs.minLA + rhs.wA, min(lhs.minWB, rhs.wB)); } }; struct edge { int v, u; edgeWeight ew; }; struct lct_edge { edgeWeight ew; edge *cirE; lct_node *whoseFirstE; path_message prevExMsg, nextExMsg; lct_edge *prevExFirstE, *nextExFirstE; void rev() { swap(prevExMsg, nextExMsg); swap(prevExFirstE, nextExFirstE); } void coverCirE(edge *e) { cirE = e; } void prevEx_setInvalid() { prevExMsg.setInvalid(); if (prevExFirstE) prevExFirstE->whoseFirstE = NULL; prevExFirstE = NULL; } void nextEx_setInvalid() { nextExMsg.setInvalid(); if (nextExFirstE) nextExFirstE->whoseFirstE = NULL; nextExFirstE = NULL; } inline void tag_prevExWB_add(int delta); inline void tag_nextExWB_add(int delta); edge *getCirE() { return this ? this->cirE : NULL; } }; struct lct_message { path_message headExMsg, tailExMsg; path_message headMsg, midMsg, tailMsg; int headExFrom, tailExFrom; int headFrom, midFrom, tailFrom; static const int FromLcHead = 1 << 0; static const int FromLcHeadEx = 1 << 1; static const int FromLcMid = 1 << 2; static const int FromLcTailEx = 1 << 3; static const int FromLcTail = 1 << 4; static const int FromLE = 1 << 5; static const int FromLCirE = 1 << 6; static const int FromMeHead = 1 << 7; static const int FromMeHeadEx = 1 << 8; static const int FromMeMid = 1 << 9; static const int FromMeTailEx = 1 << 10; static const int FromMeTail = 1 << 11; static const int FromRCirE = 1 << 12; static const int FromRE = 1 << 13; static const int FromRcHead = 1 << 14; static const int FromRcHeadEx = 1 << 15; static const int FromRcMid = 1 << 16; static const int FromRcTailEx = 1 << 17; static const int FromRcTail = 1 << 18; lct_edge *firstE, *lastE; bool hasCirE; static int preRev[1 << 19]; static void rev_from_init() { for (int i = 0; i < (1 << 19); i++) { int x = i, rx = 0; for (int j = 0; j < 19; j++) { rx = rx << 1 | (x & 1); x >>= 1; } preRev[i] = rx; } } static void rev_from(int &from) { from = preRev[from]; } void rev() { swap(firstE, lastE); swap(headExMsg, tailExMsg), swap(headExFrom, tailExFrom); swap(headMsg, tailMsg), swap(headFrom, tailFrom); rev_from(headExFrom), rev_from(tailExFrom); rev_from(headFrom), rev_from(midFrom), rev_from(tailFrom); } void coverCirE(edge *e, bool isSingle) { hasCirE = isSingle ? false : e != NULL; } void headExWB_add(int delta) { assert(headExMsg.valid()); headExMsg.wB_add(delta); } void tailExWB_add(int delta) { assert(tailExMsg.valid()); tailExMsg.wB_add(delta); } void headWB_add(int delta) { if (headExMsg.valid()) headMsg.wB_add(delta); } void midWB_add(int delta) { assert(midMsg.minWB != -1); midMsg.wB_add(delta); } void tailWB_add(int delta) { if (tailExMsg.valid()) tailMsg.wB_add(delta); } void appendL(const lct_message &lmsg) { assert(lmsg.lastE == this->firstE); lct_edge *e = lmsg.lastE; this->firstE = lmsg.firstE; this->hasCirE = lmsg.hasCirE || e->cirE || this->hasCirE; if (lmsg.tailExMsg.valid() && this->headExMsg.valid()) { path_message path1 = lmsg.tailMsg * e->ew * this->headMsg ; path_message path2 = lmsg.tailExMsg * e->cirE->ew * this->headExMsg; this->midMsg = lmsg.midMsg * (path1 + path2) * this->midMsg; if (path1.minLA < path2.minLA) this->midFrom = FromLcMid | FromLcTail | FromLE | this->headFrom | this->midFrom; else this->midFrom = FromLcMid | FromLcTailEx | FromLCirE | this->headExFrom | this->midFrom; this->headExMsg = lmsg.headExMsg; this->headExFrom = FromLcHeadEx; this->headMsg = lmsg.headMsg; this->headFrom = FromLcHead; } else if (lmsg.tailExMsg.valid()) { this->headExMsg = lmsg.headExMsg; this->headExFrom = FromLcHeadEx; this->headMsg = lmsg.headMsg; this->headFrom = FromLcHead; this->tailExMsg = lmsg.tailExMsg; this->tailExFrom = FromLcTailEx; this->tailMsg = lmsg.tailMsg * e->ew * this->midMsg; this->tailFrom = FromLcTail | FromLE | this->midFrom; this->midMsg = lmsg.midMsg; this->midFrom = FromLcMid; } else if (this->headExMsg.valid()) { this->headMsg = lmsg.midMsg * e->ew * this->headMsg; this->headFrom = FromLcMid | FromLE | this->headFrom; } else { this->headExMsg = lmsg.headExMsg; this->headExFrom = FromLcHeadEx; this->headMsg = lmsg.headMsg; this->headFrom = FromLcHead; this->midMsg = lmsg.midMsg * e->ew * this->midMsg; this->midFrom = FromLcMid | FromLE | this->midFrom; } } void appendR(const lct_message &rmsg) { assert(this->lastE == rmsg.firstE); lct_edge *e = this->lastE; this->lastE = rmsg.lastE; this->hasCirE = this->hasCirE || e->cirE || rmsg.hasCirE; if (this->tailExMsg.valid() && rmsg.headExMsg.valid()) { path_message path1 = this->tailMsg * e->ew * rmsg.headMsg ; path_message path2 = this->tailExMsg * e->cirE->ew * rmsg.headExMsg; this->midMsg = this->midMsg * (path1 + path2) * rmsg.midMsg; if (path1.minLA < path2.minLA) this->midFrom = this->midFrom | this->tailFrom | FromRE | FromRcHead | FromRcMid; else this->midFrom = this->midFrom | this->tailExFrom | FromRCirE | FromRcHeadEx | FromRcMid; this->tailExMsg = rmsg.tailExMsg; this->tailExFrom = FromRcTailEx; this->tailMsg = rmsg.tailMsg; this->tailFrom = FromRcTail; } else if (this->tailExMsg.valid()) { this->tailMsg = this->tailMsg * e->ew * rmsg.midMsg; this->tailFrom = this->tailFrom | FromRE | FromRcMid; } else if (rmsg.headExMsg.valid()) { this->headExMsg = rmsg.headExMsg; this->headExFrom = FromRcHeadEx; this->headMsg = this->midMsg * e->ew * rmsg.headMsg; this->headFrom = this->midFrom | FromRE | FromRcHead; this->tailExMsg = rmsg.tailExMsg; this->tailExFrom = FromRcTailEx; this->tailMsg = rmsg.tailMsg; this->tailFrom = FromRcTail; this->midMsg = rmsg.midMsg; this->midFrom = FromRcMid; } else { this->tailExMsg = rmsg.tailExMsg; this->tailExFrom = FromRcTailEx; this->tailMsg = rmsg.tailMsg; this->tailFrom = FromRcTail; this->midMsg = this->midMsg * e->ew * rmsg.midMsg; this->midFrom = this->midFrom | FromRE | FromRcMid; } } }; int lct_message::preRev[1 << 19]; struct lct_node { lct_node *fa, *lc, *rc; lct_edge *prevE, *nextE; lct_message msg; bool hasRev; bool hasCoveredCirE; edge *coveredCirE; int headExWBDelta, tailExWBDelta; int headWBDelta, midWBDelta, tailWBDelta; bool isRoot() { return !fa || (fa->lc != this && fa->rc != this); } void rotate() { lct_node *x = this, *y = x->fa, *z = y->fa; lct_node *b = x == y->lc ? x->rc : x->lc; x->fa = z, y->fa = x; if (b) b->fa = y; if (z && (z->lc == y || z->rc == y)) (z->lc == y ? z->lc : z->rc) = x; else { if (y->msg.firstE) { x->msg.firstE = y->msg.firstE; y->msg.firstE->whoseFirstE = x; } } if (x == y->lc) x->rc = y, y->lc = b; else x->lc = y, y->rc = b; y->update(); } void allFaTagDown() { int anc_n = 0; static lct_node *anc[MaxN]; for (lct_node *x = this; x; x = x->fa) anc[anc_n++] = x; for (int i = anc_n - 1; i >= 0; i--) anc[i]->tag_down(); } void splay() { while (!this->isRoot()) { if (!fa->isRoot()) { if ((fa->fa->lc == fa) == (fa->lc == this)) fa->rotate(); else this->rotate(); } this->rotate(); } this->update(); } void splay_until(lct_node *target) { while (this->fa != target) { if (fa->fa != target) { if ((fa->fa->lc == fa) == (fa->lc == this)) fa->rotate(); else this->rotate(); } this->rotate(); } this->update(); } void access() { this->allFaTagDown(); for (lct_node *p = this, *q = NULL; p; q = p, p = p->fa) { p->splay(); lct_edge *qFirstE = q ? q->msg.firstE : NULL; if (qFirstE->getCirE()) { if (qFirstE->getCirE() != p->prevE->getCirE()) { if (qFirstE->prevExFirstE) { lct_node *pr = qFirstE->getCirE() == p->nextE->getCirE() ? p->rc : qFirstE->prevExFirstE->whoseFirstE; qFirstE->prevExMsg = qFirstE->prevExFirstE->ew * pr->msg.headMsg * pr->msg.headExMsg; } else qFirstE->prevExMsg.setEmpty(); } else { qFirstE->prevExMsg.setInvalid(); qFirstE->prevExFirstE = NULL; } } if (p->prevE->getCirE()) { if (p->prevE->getCirE() == qFirstE->getCirE()) { p->prevE->nextEx_setInvalid(); qFirstE->prevEx_setInvalid(); } if (p->prevE->getCirE() == p->nextE->getCirE()) { p->prevE->nextExMsg = p->rc->msg.headExMsg * p->rc->msg.headMsg * p->nextE->ew; p->prevE->nextExFirstE = p->nextE; p->nextE->prevExFirstE = p->prevE; } } if (p->rc) { if (p->rc->msg.firstE) p->rc->msg.firstE->whoseFirstE = p->rc; } p->nextE = qFirstE; p->rc = q; if (qFirstE) qFirstE->whoseFirstE = NULL; } this->splay(); } void makeRoot() { this->access(); this->tag_rev(); this->tag_down(); } lct_node *findRoot() { this->access(); lct_node *root = this; while (root->tag_down(), root->lc) root = root->lc; root->splay(); return root; } void tag_rev() { hasRev = !hasRev; msg.rev(); swap(headExWBDelta, tailExWBDelta); swap(headWBDelta, tailWBDelta); } void tag_coverCirE(edge *e) { hasCoveredCirE = true, coveredCirE = e; msg.coverCirE(e, !lc && !rc); } void tag_headExWB_add(int delta) { headExWBDelta += delta; msg.headExWB_add(delta); } void tag_tailExWB_add(int delta) { tailExWBDelta += delta; msg.tailExWB_add(delta); } void tag_headWB_add(int delta) { headWBDelta += delta; msg.headWB_add(delta); } void tag_midWB_add(int delta) { midWBDelta += delta; msg.midWB_add(delta); } void tag_tailWB_add(int delta) { tailWBDelta += delta; msg.tailWB_add(delta); } void tag_wB_add_all(int all, int delta) { for (int cur = all; cur > 0; cur &= cur - 1) { const int P = 29; switch ((cur & -cur) % P) { case lct_message::FromLcHead % P: lc->tag_headWB_add(delta); break; case lct_message::FromLcHeadEx % P: lc->tag_headExWB_add(delta); break; case lct_message::FromLcMid % P: lc->tag_midWB_add(delta); break; case lct_message::FromLcTailEx % P: lc->tag_tailExWB_add(delta); break; case lct_message::FromLcTail % P: lc->tag_tailWB_add(delta); break; case lct_message::FromLE % P: prevE->ew.wB += delta; break; case lct_message::FromLCirE % P: prevE->cirE->ew.wB += delta; break; case lct_message::FromMeHead % P: break; case lct_message::FromMeHeadEx % P: prevE->tag_nextExWB_add(delta); break; case lct_message::FromMeMid % P: break; case lct_message::FromMeTailEx % P: nextE->tag_prevExWB_add(delta); break; case lct_message::FromMeTail % P: break; case lct_message::FromRCirE % P: nextE->cirE->ew.wB += delta; break; case lct_message::FromRE % P: nextE->ew.wB += delta; break; case lct_message::FromRcHead % P: rc->tag_headWB_add(delta); break; case lct_message::FromRcHeadEx % P: rc->tag_headExWB_add(delta); break; case lct_message::FromRcMid % P: rc->tag_midWB_add(delta); break; case lct_message::FromRcTailEx % P: rc->tag_tailExWB_add(delta); break; case lct_message::FromRcTail % P: rc->tag_tailWB_add(delta); break; } } } void tag_down() { if (hasRev) { swap(lc, rc); swap(prevE, nextE); if (lc) { prevE->rev(); lc->tag_rev(); } if (rc) { nextE->rev(); rc->tag_rev(); } hasRev = false; } if (hasCoveredCirE) { if (lc) { prevE->coverCirE(coveredCirE); lc->tag_coverCirE(coveredCirE); } if (rc) { nextE->coverCirE(coveredCirE); rc->tag_coverCirE(coveredCirE); } hasCoveredCirE = false; } if (headExWBDelta != 0) { tag_wB_add_all(msg.headExFrom, headExWBDelta); headExWBDelta = 0; } if (tailExWBDelta != 0) { tag_wB_add_all(msg.tailExFrom, tailExWBDelta); tailExWBDelta = 0; } if (headWBDelta != 0) { tag_wB_add_all(msg.headFrom, headWBDelta); headWBDelta = 0; } if (midWBDelta != 0) { tag_wB_add_all(msg.midFrom, midWBDelta); midWBDelta = 0; } if (tailWBDelta != 0) { tag_wB_add_all(msg.tailFrom, tailWBDelta); tailWBDelta = 0; } } void update() { if (prevE) { msg.headExMsg = prevE->nextExMsg; msg.headExFrom = lct_message::FromMeHeadEx; } else { msg.headExMsg.setInvalid(); msg.headExFrom = 0; } msg.headMsg.setEmpty(); msg.headFrom = lct_message::FromMeHead; if (nextE) { msg.tailExMsg = nextE->prevExMsg; msg.tailExFrom = lct_message::FromMeTailEx; } else { msg.tailExMsg.setInvalid(); msg.tailExFrom = 0; } msg.tailMsg.setEmpty(); msg.tailFrom = lct_message::FromMeTail; msg.midMsg.setEmpty(); msg.midFrom = lct_message::FromMeMid; msg.hasCirE = false; msg.firstE = prevE, msg.lastE = nextE; if (lc) this->msg.appendL(lc->msg); if (rc) this->msg.appendR(rc->msg); } }; void lct_edge::tag_prevExWB_add(int delta) { if (!prevExMsg.valid()) return; prevExMsg.wB_add(delta); if (prevExFirstE) { lct_node *p = prevExFirstE->whoseFirstE; assert(p != NULL); p->tag_headWB_add(delta); p->tag_headExWB_add(delta); prevExFirstE->ew.wB += delta; } } void lct_edge::tag_nextExWB_add(int delta) { if (!nextExMsg.valid()) return; nextExMsg.wB_add(delta); if (nextExFirstE) { lct_node *p = nextExFirstE->whoseFirstE; assert(p != NULL); p->tag_headWB_add(delta); p->tag_headExWB_add(delta); nextExFirstE->ew.wB += delta; } } lct_node lctVer[MaxN + 1]; BlockAllocator<edge> lctCirEAllocator; BlockAllocator<lct_edge> lctEAllocator; int n; void cactus_init() { lct_message::rev_from_init(); for (int v = 1; v <= n; v++) { lct_node *p = lctVer + v; p->fa = p->lc = p->rc = NULL; p->prevE = p->nextE = NULL; p->hasRev = false; p->hasCoveredCirE = false; p->headExWBDelta = 0, p->tailExWBDelta = 0; p->headWBDelta = 0, p->midWBDelta = 0, p->tailWBDelta = 0; p->update(); } } bool cactus_link(int v, int u, int wA, int wB) { if (v == u) return false; if (v > u) swap(v, u); edgeWeight ew(wA, wB); lct_node *x = lctVer + v, *y = lctVer + u; x->makeRoot(); y->makeRoot(); if (x->fa) { x->access(); y->allFaTagDown(), y->splay_until(x); if (x->msg.hasCirE) return false; edge *cirE = lctCirEAllocator.allocate(); cirE->v = v, cirE->u = u, cirE->ew = ew; y->nextE->cirE = cirE; y->nextE->prevExMsg.setEmpty(); y->nextE->prevExFirstE = NULL; x->prevE->cirE = cirE; x->prevE->nextExMsg.setEmpty(); x->prevE->nextExFirstE = NULL; if (y->rc) y->rc->tag_coverCirE(cirE); y->update(), x->update(); } else { x->fa = y; lct_edge *e = lctEAllocator.allocate(); e->ew = ew, e->cirE = NULL; e->prevEx_setInvalid(), e->nextEx_setInvalid(); x->prevE = e; e->whoseFirstE = x; x->update(); } return true; } bool cactus_cut(int v, int u, int wA, int wB) { if (v == u) return false; if (v > u) swap(v, u); static int cnt = 0; cnt++; edgeWeight ew(wA, wB); lct_node *x = lctVer + v, *y = lctVer + u; if (x->findRoot() != y->findRoot()) return false; y->makeRoot(); x->access(); y->allFaTagDown(), y->splay_until(x); lct_edge *e = y->nextE; edge *cirE = e->cirE; if (cirE && cirE->v == v && cirE->u == u && cirE->ew == ew) { y->nextE->cirE = NULL, y->nextE->prevEx_setInvalid(); x->prevE->cirE = NULL, x->prevE->nextEx_setInvalid(); if (y->rc) y->rc->tag_coverCirE(NULL); y->update(), x->update(); lctCirEAllocator.deallocate(cirE); return true; } if (!y->rc && e->ew == ew) { lct_node *ex, *ey; if (cirE) { ex = lctVer + cirE->v; ey = lctVer + cirE->u; ey->makeRoot(); ex->access(); ey->allFaTagDown(), ey->splay_until(ex); ey->nextE->cirE = NULL, ey->nextE->prevEx_setInvalid(); ex->prevE->cirE = NULL, ex->prevE->nextEx_setInvalid(); if (ey->rc) ey->rc->tag_coverCirE(NULL); ey->update(), ex->update(); y->makeRoot(); x->access(); y->allFaTagDown(), y->splay_until(x); } lctEAllocator.deallocate(e); x->prevE = NULL, y->nextE = NULL; x->lc = NULL, y->fa = NULL; x->update(), y->update(); if (y->msg.firstE) y->msg.firstE->whoseFirstE = y; if (cirE) { ex->makeRoot(), ey->makeRoot(); ex->fa = ey; e = lctEAllocator.allocate(); e->ew = cirE->ew, e->cirE = NULL; e->prevEx_setInvalid(), e->nextEx_setInvalid(); ex->prevE = e; e->whoseFirstE = ex; ex->update(); lctCirEAllocator.deallocate(cirE); } return true; } return false; } bool cactus_add(int qv, int qu, int delta) { lct_node *x = lctVer + qv, *y = lctVer + qu; if (x->findRoot() != y->findRoot()) return false; x->makeRoot(); y->access(); if (y->msg.midMsg.minWB == -1) return false; y->tag_midWB_add(delta); return true; } path_message cactus_query(int qv, int qu) { lct_node *x = lctVer + qv, *y = lctVer + qu; path_message res; if (x->findRoot() != y->findRoot()) { res.setInvalid(); return res; } x->makeRoot(); y->access(); return y->msg.midMsg; } int main() { int nQ; cin >> n >> nQ; cactus_init(); while (nQ--) { char type; while (type = getchar(), type != 'l' && type != 'c' && type != 'a' && type != 'd'); if (type == 'l') { int v = getint(), u = getint(), wA = getint(), wB = getint(); if (cactus_link(v, u, wA, wB)) printf("ok\n"); else printf("failed\n"); } else if (type == 'c') { int v = getint(), u = getint(), wA = getint(), wB = getint(); if (cactus_cut(v, u, wA, wB)) printf("ok\n"); else printf("failed\n"); } else if (type == 'a') { int v = getint(), u = getint(), delta = getint(); if (cactus_add(v, u, delta)) printf("ok\n"); else printf("failed\n"); } else if (type == 'd') { int v = getint(), u = getint(); path_message ret = cactus_query(v, u); printf("%d %d\n", ret.minLA, ret.minWB); } else { puts("error!"); } } return 0; }