【BZOJ5212】[ZJOI2018] 历史(LCT大黑题)

时间:2022-11-05 12:30:28

点此看题面

大致题意: 给定一棵树每个节点\(Access\)的次数,求最大虚实链切换次数,带修改。

什么是\(Access\)?

推荐你先去学一学\(LCT\)吧。

初始化(不带修改的做法)

首先考虑初始化,即不带修改的做法,貌似这样就有30分了。

先要注意到一点:我们可以发现,对于每一个节点,它的答案是独立的,且只受其子树内的节点影响。

这么一说,应该就不难想到树形\(DP\)了吧。

如果有两个相邻\(Access\)操作,显然当且仅当这两次\(Access\)来自于当前节点的两个不同子节点的子树,才会对答案造成贡献。

那么如何快速求出答案呢?

设\(tot_i\)为以\(i\)为根的子树内的\(Access\)操作总次数,\(Sum=\sum tot_{son}\),\(Max=max\{tot_{son}\}\)。

则对于当前节点的答案有两种情况:

  1. \(2*Max\le Sum\)。

    则显然在最优情况下我们可以每次选择两个来自不同子树的节点作为相邻的\(Access\)节点,因此每一次\(Access\)都会对答案造成贡献。因此此时的答案就是\(Sum-1\)。

    例如:\(tot_x=3,tot_y=4,tot_z=5\),我们可以这样操作:\(xyxzxzyzyzyz\)。

  2. \(2*Max>Sum\)。

    此时我们每次必然先每次选择一个来自非\(Max\)节点子树内的节点与一个来自\(Max\)节点子树内的节点作为相邻的\(Access\)节点,然后将多余的来自\(Max\)节点子树内的节点全部放在最后面,这样一来造成贡献就是非\(Max\)节点子树内的\(Access\)操作总次数\(*2\),即\(2(Sum-Max)\)。

    例如:\(tot_x=2,tot_y=3,tot_z=7\),我们可以这样操作:\(xzxzyzyzyzzz\)。易发现只有前面\(10\)次操作有贡献。

这样,我们就可以完成初始化了。

考虑修改

既然涉及到\(Access\)操作,我们自然要用\(LCT\)来做这道题啦... ...

只不过这里的\(LCT\)的\(Access\)函数要加上一大堆的判断。

这里要给每个节点维护\(3\)个值:\(Val,Sum,Calc\)。其中\(Val\)存储当前节点的\(Access\)次数,\(Sum\)存储以在实际树中以当前节点为根的子树在\(Splay\)中以当前节点的左儿子为根的子树的\(Access\)操作总数(包括当前节点),\(Calc\)存储在实际树中以当前节点为根的子树除去在\(Splay\)中以当前节点的右儿子为根的子树后的\(Access\)操作总数。

这样一来,\(PushUp\)时就可以得到当前节点的\(Sum\)就等于其左右儿子\(Sum\)之和加上当前节点的\(Val\)和\(Calc\)。

对于每一个节点,我们将其向\(Access\)操作总数\(*2>\)当前节点\(Access\)操作总数的子节点连一条实边(若没有则不连),其余节点连虚边

下面,让我们来考虑如何\(Access\)。

我们需要记三个变量:\(x\)表示当前操作节点,\(son\)表示上一个操作节点,\(val\)表示当前增加的\(Access\)次数

首先,将\(x\)先\(Splay\)到根。

然后,我们用一个变量\(Sum\)存下\(x\)的\(Sum\)值减去其左儿子的\(Sum\)值,这样一来就得到实际树中以\(x\)为根的子树的\(Access\)操作总数了。

接下来,我们将\(ans\)减去当前子树原先的贡献。

其次,将\(Sum\),当前节点的\(Sum\),以及当前节点的\(Val/Calc\)(如果为被修改节点则更新\(Val\),如果为被修改节点的祖先节点,则更新\(Calc\))各加上\(val\)。

下一步,我们判断\(son\)是否为当前节点的实儿子,如果是,则更新。注意更新时我们要将当前节点\(Calc\)先加上原右儿子的\(Sum\),然后减去\(son\)的\(Sum\)。

最后,我们将\(ans\)加上当前节点的新答案,然后继续处理其父节点即可。

代码

#include<bits/stdc++.h>
#define Type template<typename I>
#define N 400000
#define LL long long
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,ee,a[N+5],lnk[N+5];
struct edge
{
int to,nxt;
}e[(N<<1)+5];
class Class_FIO
{
private:
#define Fsize 100000
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
#define pc(ch) (FoutSize^Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
#define Isi(x) (typeid(x).name()==typeid(1).name()||typeid(x).name()==typeid(1LL).name())
#define Isc(x) (typeid(x).name()==typeid('a').name())
int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
public:
Class_FIO() {A=B=Fin;}
Type inline void read(I& x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
Type inline void write(I x)
{
if(Isi(x)) {while(Stack[++Top]=x%10+48,x/=10);while(Top) pc(Stack[Top--]);}
if(Isc(x)) pc(x);
}
template<typename I,typename... A> inline void read(I& x,A&... y) {read(x),read(y...);}
template<typename I,typename... A> inline void write(I x,A... y) {write(x),write(y...);}
inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
}F;
class Class_LCT
{
private:
#define SIZE N
#define PushUp(x) (node[x].Sum=node[x].Val+node[x].Calc+node[node[x].Son[0]].Sum+node[node[x].Son[1]].Sum)
#define Rever(x) (swap(node[x].Son[0],node[x].Son[1]),node[x].Rev^=1)
#define PushDown(x) (node[x].Rev&&(Rever(node[x].Son[0]),Rever(node[x].Son[1]),node[x].Rev=0))
#define Which(x) (node[node[x].Father].Son[1]==x)
#define Connect(x,y,d) (node[node[x].Father=y].Son[d]=x)
#define IsRoot(x) (node[node[x].Father].Son[0]^x&&node[node[x].Father].Son[1]^x)
LL ans;int Stack[SIZE+5];
struct Tree
{
int Op,Father,Rev,Son[2];LL Val,Calc,Sum;
}node[SIZE+5];
inline void Rotate(int x)
{
register int fa=node[x].Father,pa=node[fa].Father,d=Which(x);
!IsRoot(fa)&&(node[pa].Son[Which(fa)]=x),node[x].Father=pa,Connect(node[x].Son[d^1],fa,d),Connect(fa,x,d^1),PushUp(fa),PushUp(x);
}
inline void Splay(int x)
{
register int fa=x,Top=0;
while(Stack[++Top]=fa,!IsRoot(fa)) fa=node[fa].Father;
while(Top) PushDown(Stack[Top]),--Top;
while(!IsRoot(x)) fa=node[x].Father,!IsRoot(fa)&&(Rotate(Which(x)^Which(fa)?x:fa),0),Rotate(x);
}
public:
inline void Init(int x)//初始化
{
register int i,v,Pos=x;register LL Max=node[x].Val=a[x];
for(i=lnk[x];i;i=e[i].nxt) (v=e[i].to)^node[x].Father&&(node[v].Father=x,Init(v),node[x].Calc+=node[v].Sum,Max<node[v].Sum&&(Max=node[Pos=v].Sum));
if(node[x].Sum=node[x].Calc+node[x].Val,(Max<<1)<=node[x].Sum) return (void)(ans+=node[x].Sum-1,node[x].Op=1);//若2*Max<=Sum,则为第一种情况,将ans加上Sum-1,并标记op为1,然后退出函数
ans+=node[x].Sum-Max<<1,x^Pos?(node[x].Calc-=node[node[x].Son[1]=Pos].Sum,node[x].Op=2):(node[x].Op=3);//否则,将ans加上2*(Sum-Max),若找到的不是这个节点本身,则为第二种情况,更新Calc,并标记op为2,否则标记op为3
}
inline void Update(int x,int son,int val)//修改
{
Splay(x);//将其旋转到根
register LL Sum=node[x].Sum-node[node[x].Son[0]].Sum;//记录除去祖先贡献后当前子树内的贡献
ans-=node[x].Op^1?Sum-(node[x].Op^3?node[node[x].Son[1]].Sum:node[x].Val)<<1:Sum-1,//减去原先的贡献
Sum+=val,node[x].Sum+=val,son?(node[x].Calc+=val):(node[x].Val+=val),//修改值
(node[son].Sum<<1)>Sum&&(node[x].Calc+=node[node[x].Son[1]].Sum,node[x].Calc-=node[node[x].Son[1]=son].Sum);
if((node[node[x].Son[1]].Sum<<1)>Sum) {ans+=Sum-node[node[x].Son[1]].Sum<<1,node[x].Op=2;goto loop;}
node[x].Son[1]&&(node[x].Calc+=node[node[x].Son[1]].Sum,node[x].Son[1]=0);
if((node[x].Val<<1)>Sum) {ans+=Sum-node[x].Val<<1,node[x].Op=3;goto loop;}
ans+=Sum-1,node[x].Op=1;
loop:node[x].Father&&(Update(node[x].Father,x,val),0);//继续处理祖先
}
inline LL GetAns() {return ans;}//返回答案
}LCT;
int main()
{
register int query_tot,i,x,y;
for(F.read(n,query_tot),i=1;i<=n;++i) F.read(a[i]);
for(i=1;i<n;++i) F.read(x,y),add(x,y),add(y,x);
LCT.Init(1),F.write(LCT.GetAns(),'\n');
while(query_tot--) F.read(x,y),LCT.Update(x,0,y),F.write(LCT.GetAns(),'\n');
return F.clear(),0;
}

【BZOJ5212】[ZJOI2018] 历史(LCT大黑题)的更多相关文章

  1. Luogu4338 ZJOI2018 历史 LCT、贪心

    传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...

  2. BZOJ5212 ZJOI2018历史(LCT)

    首先相当于最大化access的轻重边交换次数. 考虑每个点作为战场(而不是每个点所代表的国家与其他国家交战)对答案的贡献,显然每次产生贡献都是该点的子树内(包括自身)此次access的点与上次acce ...

  3. P4338 &lbrack;ZJOI2018&rsqb;历史 LCT&plus;树形DP

    \(\color{#0066ff}{ 题目描述 }\) 这个世界有 n 个城市,这 n 个城市被恰好 \(n-1\) 条双向道路联通,即任意两个城市都可以 互相到达.同时城市 1 坐落在世界的中心,占 ...

  4. &lbrack;BZOJ5212&rsqb;&lbrack;ZJOI2018&rsqb;历史

    传送门(洛谷) 人生第一道九条可怜……神仙操作…… 看着题解理解了一个早上才勉强看懂怎么回事…… 简化一下题目就是:已知每一个点access的总次数,求一个顺序使虚实边转化的次数最多 考虑一下,对于x ...

  5. 【BZOJ5212】&lbrack;ZJOI2018&rsqb;历史(Link-Cut Tree)

    [BZOJ5212][ZJOI2018]历史(Link-Cut Tree) 题面 洛谷 BZOJ 题解 显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数. 先考 ...

  6. &lbrack;ZJOI2018&rsqb;历史

    [ZJOI2018]历史 最大化access轻重链的切换次数 考虑一个点的贡献,即它交换重儿子的次数 发现这个次数只和它自己ai以及每个儿子的子树次数和有关. 一个关键的事实是: 我们可以自上而下进行 ...

  7. &lbrack;ZJOI2018&rsqb;历史(LCT)

    这篇还发了洛谷题解 [Luogu4338] [BZOJ5212] 题解 题意 给出一棵树,给定每一个点的 \(access\) 次数,计算轻重链切换次数的最大值,带修改. 先考虑不带修改怎么做 假设 ...

  8. 洛谷P4338 &lbrack;ZJOI2018&rsqb;历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  9. LOJ2434&period; 「ZJOI2018」历史 &lbrack;LCT&rsqb;

    LOJ 思路 第一眼看似乎没有什么思路,试着套个DP上去:设\(dp_x\)表示只考虑\(x\)子树,能得到的最大答案. 合并的时候发现只有\(x\)这个点有可能做出新的贡献,而做出新贡献的时候必然是 ...

随机推荐

  1. Js 对 浏览器 的 URL的操作

    下面是一些实例: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  2. VisualSFM for Structure from Motion

    VisualSFM是Changchang Wu编写的使用 Structure from Motion (SfM)进行3D重建的交互界面,具体内容详见http://homes.cs.washington ...

  3. 记小白的一次基于vue&plus;express&plus;mongodb个人站开发

    学了vue和node一段时间了,折腾了一些零零散散的小东西.马上大四了要出去找工作了,所以早就想搭一个个人站作为一次较为全面的总结.因为没有设计功底,界面设计使我这种强迫症患者苦不堪言.幸而到最后花了 ...

  4. Smart line Panel和S7-200的MPI通信

    1.系统组成 2.一个简单任务 3.设置S7-200的通信参数 1)新建工程,设置CPU类型 2)设置端口1的通讯参数PLC地址为2,波特率187.5kbps 组态 3)保存完成配置 4.组态Smar ...

  5. 3ds max学习笔记(三)--视点显示控制

    显示模式:1.模型一般是以实体方式显示的,若想看线框方式,摁F3:返回实体,摁F3:2.实体加线框模式显示,摁F4:返回,摁F4:3.透明效果:ALT+X,透明显示,之后F4,显示线框:程序内的其他显 ...

  6. Spring Boot &plus; Spring Cloud 实现权限管理系统 后端篇(二十三):配置中心(Config、Bus)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 技术背景 如今微服务架构盛行,在分布式系统中,项目日益庞大,子项目日益增多,每 ...

  7. 联机分析处理ROLAP、MOLAP和HOLAP区别&lpar;转&rpar;

    OLAP(on-Line Analysis Processing)是使分析人员.管理人员或执行人员能够从多角度对信息进行快速.一致.交互地存取,从而获得对数据的更深入了解的一类软件技术.OLAP的目标 ...

  8. Spring事务超时、回滚的相关说明

    事务超时: @Transactional(timeout = 60) 如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数 ...

  9. 【Oracle 12c】CUUG OCP认证071考试原题解析(29)

    29.choose the best answer Evaluate the following query: SQL> SELECT promo_name || q'{'s start dat ...

  10. 四元数(Quaternion)和旋转 &plus;欧拉角

    四元数介绍 旋转,应该是三种坐标变换--缩放.旋转和平移,中最复杂的一种了.大家应该都听过,有一种旋转的表示方法叫四元数.按照我们的习惯,我们更加熟悉的是另外两种旋转的表示方法--矩阵旋转和欧拉旋转. ...