首页 > 代码库 > BZOJ3159:决战

BZOJ3159:决战

这题网上题解比较少……

于是本蒟蒻就来写一发吧

~~~~~吐槽时间~~~~~

这种题属于太弱布吉岛,普通人不会做,太神更不做的题目……

Katharon#1题解中似乎讲的蛮详细的,不过我是自己YY的……

因为提前被剧透是Splay+LCT,于是本蒟蒻看了半天布吉岛那个据点顶个鸟用,题解表示,我这是给你们降低难度!

或许是为了减小常数?不过BZOJ上无压力~

不开long long毁一生

~~~~~吐槽时间结束~~~~~

首先我们把那个据点扔到一边,直接解决更难的问题,任意选两个点,翻转它们之间的权值

因为我们要翻转路径权值,所以显然需要一个Splay

(Treap不行?均摊分析会跪吧……)

然后用什么维护外层呢?

树链剖分?不行,因为我们需要提取一点路径

那就LCT了!

有LCT还要神马Splay,直接翻转不就好?您这样翻转的是一条链……

 

此处我们再围观一下LCT

LCT是用一堆Splay维护链剖分,每个链以深度为关键字用Splay维护起来,链的Splay的根上记录着它的父亲,即树链剖分中的pre[top[x]]

然后我们定义

splay(x):操作完之后使得x为所在链的Splay的根

access(x):操作完之后使得x在主链且x在链尾,即只提取root和x之间的路径

reroot(x):操作完之后使得x在主链且x在链首,即让x变为根

因为不同的LCT实现可能不同,所以我们这里的操作按这个定义来实现(学过LCT的就可以按这个要求实现)

 

然后另外的Splay在哪里?我们把它记录在一条链的根节点上,上面维护着这条链对应的Splay的根节点(同样以深度为关键字,所以是一一对应的,当然,深度不需要在程序中体现)

 

splay操作是基本,我们先解决它

注意到只有根节点上记录的对应Splay节点是有效的,所以我们只需要在最后一步rotate时将根节点的值赋给x就好了

因为借助access和reroot,我们可以实现提取链的功能,接下来只要解决这两个,这题就做完了

因为显然reroot(x)=access(x)+reverse(x),所以我们只需要解决reverse,解决方案很显然,把对应的Splay也翻转一遍就是了(所以说Splay也是以深度为关键字)

然后就是access了

access的代码可以是这样 for (cut(p);p->pre!=null;splay(p)) cut(p->pre), p->pre->ch[1]=p, link(p->pre);

当然,对于最普通的LCT操作,cut(p)=splay(p) link(p)={};

 

以下操作大方针:时刻保证链的根节点记录的对应Splay节点也是根

 

在这里就有些不同了,cut/link的同时,会导致根节点的记录节点有变化,什么变化呢?

cut的变化是删除了右儿子的一部分并将那部分赋给右儿子(右儿子也成为了链的根节点)

对应到Splay当中就是split操作,不过从哪split呢?在LCT上记录一个size就可以解决

link的变化是增加了右儿子的一部分,对应到Splay当中就是merge操作

 

因为本题接下来就是Splay和LCT的姿势问题,所以代码自行YY吧~

BZOJ3159:决战