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

时间:2023-12-16 13:08:02

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

题面

洛谷

BZOJ

题解

显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数。

先考虑不带修改的答案。

如果直接考虑全局的答案会很麻烦。

考虑每一个在每一个点处被切换的次数。

显然这个子树之和其子树内的点的\(access\)次数相关,和子树外的点无关。

而在这个点处被切换只有它的子树中不在同一棵子树内的两个点先后进行\(access\)。

对于一个点统计其不同子树内的\(access\)次数和,那么对于当前点而言,等价于有子树个数种颜色,每种颜色可以使用若干次,求一个放置方案数,使得相邻颜色不同的组数最多。

这个东西貌似是场\(agc\)的\(a\)题????

假设颜色总和是\(s\),最大值为\(p\),那么答案就是\(min\{s-1,2*(s-p)\}\)

原因大概就是看看出现次数最多的那个多不多,如果不是很多的话就可以穿插的放。(强行解释.jpg)

那么这样子可以进行一次\(dfs\),\(O(n)\)的算出答案。

然后现在存在了修改操作。

首先发现这个东西是一个加法操作,然后发现会影响的只有到达根的这一段路径。

那么考虑一下\(s-1<2*(s-p)\)推出来是\(2p<s+1\)。

而这样一次操作之后,路径上的\(s\)会做加法,而\(p\)呢????

来一点骚操作,我们对于\(2s_v\le s_u+1\)的、存在父子关系的两个点\(u,v\)之间连上一条轻边,在\(2s_v>s_u+1\)的\(u,v\)之间连上一条重边。

然后我们似乎就完成了一次重链剖分,可以证明每个点只会拥有不超过一个重儿子。

那么也可以发现到根节点的轻边数量是\(log\)级别的。

继续看看,发现进行区间加法的时候,重儿子显然还是满足上述条件,直接跳过不需要考虑。

那么对于一个轻儿子特殊的考虑一下是否需要修改其状态。

那么动态的修改轻重儿子就是\(LCT\)可以完成的事情啦。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 400400
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,m,hson[MAX];
ll a[MAX],s[MAX],ss[MAX],ans;
struct Node{int ch[2],ff;}t[MAX];
bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
void pushup(int x){s[x]=s[t[x].ch[0]]+s[t[x].ch[1]]+ss[x]+a[x];}
void rotate(int x)
{
int y=t[x].ff,z=t[y].ff;
int k=t[y].ch[1]==x;
if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
t[x].ch[k^1]=y;t[y].ff=x;
pushup(y);pushup(x);
}
void Splay(int x)
{
while(!isroot(x))
{
int y=t[x].ff,z=t[y].ff;
if(!isroot(y))
(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}
}
void dfs(int u,int ff)
{
ll mx=a[u];int ms=u;s[u]=a[u];t[u].ff=ff;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
{
dfs(e[i].v,u),s[u]+=s[e[i].v];
if(s[e[i].v]>mx)mx=s[e[i].v],ms=e[i].v; }
ss[u]=s[u]-a[u];
if(2*mx>s[u])
{
ans+=(s[u]-mx)*2;
if(ms==u)hson[u]=0;
else hson[u]=1,t[u].ch[1]=ms,ss[u]-=mx;
}
else ans+=s[u]-1,hson[u]=2;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
dfs(1,0);printf("%lld\n",ans);
while(m--)
{
int x=read(),w=read();
for(int y=0;x;y=x,x=t[x].ff)
{
Splay(x);
ll S=s[x]-s[t[x].ch[0]];
if(hson[x]==0)ans-=(S-a[x])*2;
if(hson[x]==1)ans-=(S-s[t[x].ch[1]])*2;
if(hson[x]==2)ans-=S-1;
S+=w;s[x]+=w;if(y)ss[x]+=w;else a[x]+=w;
if(s[y]*2>S)ss[x]+=s[t[x].ch[1]],ss[x]-=s[y],t[x].ch[1]=y;
if(s[t[x].ch[1]]*2>S)hson[x]=1,ans+=(S-s[t[x].ch[1]])*2;
else
{
if(t[x].ch[1])ss[x]+=s[t[x].ch[1]],t[x].ch[1]=0;
if(a[x]*2>S)hson[x]=0,ans+=(S-a[x])*2;
else hson[x]=2,ans+=S-1,t[x].ch[1]=0;
}
}
printf("%lld\n",ans);
}
return 0;
}