点此看题面

大致题意: 给定一棵树每个节点\(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 [ZJOI2018]历史 LCT+树形DP

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

  4. [BZOJ5212][ZJOI2018]历史

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

  5. 【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)

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

  6. [ZJOI2018]历史

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

  7. [ZJOI2018]历史(LCT)

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

  8. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

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

  9. LOJ2434. 「ZJOI2018」历史 [LCT]

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

随机推荐

  1. mysql隔离级别与锁,接口并发响应速度的关系(1)

    默认隔离级别:可重复读 原始数据 | id | name | addr | | nick | NULL | 事务1 事务2 start transaction start transaction ; ...

  2. 2.2 Go 常量与枚举

    package main import ( "fmt" "math" ) //常量 func cons(){ , //常量的数字在不明确指定类型的情况下,即可以 ...

  3. my23_pxc其中一个节点重建记录

    PXC报废了一个节点,时间大概在周五,而此时故障的数据库节点比较多,警告信息也成百上千,此信息混合于已有的故障节点信息中,没有被及时发现:然后周六.周日各报废一个,在周一的时候,业务已经没有节点可以写 ...

  4. php数组·的方法-数组检索

    /* * //数组检索函数 * */ //array_keys() 获取数组中所有键名 //array_values() 获取数组中所有键名 $arr6=range('a','e'); print_r ...

  5. 谷歌Chrome浏览器离线安装包

    下载地址(自选版本) 链接: https://pan.baidu.com/s/1_gVP32tBNTR0pHhQbbM8Iw 密码: rmak 有能力的可以自行到下方地址下载: 最新稳定版:https ...

  6. my.资料__烹饪炼药

    ZC: 新区的时候,烹饪炼药 也不是 等级越高越好:等级越高需要的人可能少,价格高 但是买的人少:大家都在那个等级 很容易堵车 没有摊位... 刚开始 大家都穷 可能等级稍低的 反而好卖... ZC: ...

  7. java多线程(三)

    1.1什么的多线程的安全问题? 多个线程对共享资源进行访问时,引起共享资源不一致的问题.   1.2一般解决多线程安全问题的解决方案有哪些? 1.2.1 同步方法  public synchroniz ...

  8. java替换word

    package demo; import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOExcept ...

  9. My first Python program(附增加清屏方法)

    #TempConvert.py TempStr = input("请输入带有符号的温度值:") if TempStr[-1] in ['F', 'f']: C = (eval(Te ...

  10. .vimrc的配置

    set nocompatible set encoding=utf-8 set fileencodings=utf-8,chinese set tabstop=4 set cindent shiftw ...