题意
给一棵n个节点的树,每个节点有一个access次数
ax
a
x
。现在可以以任意顺序来access每个节点,要求轻重链切换次数总和最大。同时还有修改,每次会把a[x]加上一个正整数。
n<=400000
分析
我们考虑一个节点x的轻重链切换次数。
不难发现只有当相邻的两次操作不在同一棵子树中或一次为x另一次不为x时有1的贡献。
如果我们把
ax
a
x
看做
A0
A
0
,第i棵子树大小看做
Ai
A
i
,那么可以看成现在有
t=∑ki=0Ai
t
=
∑
i
=
0
k
A
i
个小球,每个小球有一种颜色。现在要求以某种顺序摆放小球,使得相邻且不同颜色的小球对数尽量大。
设
h=maxki=0Ai
h
=
max
i
=
0
k
A
i
,不难发现答案就等于
min(t−1,2(t−h))
m
i
n
(
t
−
1
,
2
(
t
−
h
)
)
,也就是当
2h≥t+1
2
h
≥
t
+
1
时会取到后者。
现在我们已经可以轻易求出m=0时的答案了,考虑如何支持修改操作。
设
fi
f
i
表示i子树中的access次数和。
当
2fi≥ffa+1
2
f
i
≥
f
f
a
+
1
时,我们把
(fa,i)
(
f
a
,
i
)
这条边设为重边,其余设为轻边。
不难发现每个点往下最多只有一条重边,且每个点到根路径上的轻边数不超过
O(log∑ai)
O
(
l
o
g
∑
a
i
)
当现在要把
a[x]
a
[
x
]
加上
w
w
时,显然重边仍然是重边,因为没有改变而
h
h
增大了;而轻边则有可能变成重边。
那么我们就像lct的access操作那样去维护每条轻边即可。
时间复杂度
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=400004;
int n,m,cnt,last[N];
struct edge{int to,next;}e[N*2];
LL ans,f[N],a[N],b[N];
struct tree{int l,r,fa,s;LL f,tag;}t[N];
bool leaf[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
bool is_root(int x)
{
return x!=t[t[x].fa].l&&x!=t[t[x].fa].r;
}
void updata(int x)
{
t[x].s=t[t[x].l].s+t[t[x].r].s+1;
}
void mark(int x,LL y)
{
t[x].tag+=y;t[x].f+=y;
}
void pushdown(int x)
{
LL w=t[x].tag;t[x].tag=0;
if (t[x].l) mark(t[x].l,w);
if (t[x].r) mark(t[x].r,w);
}
void remove(int x)
{
if (!is_root(x)) remove(t[x].fa);
pushdown(x);
}
void rttl(int x)
{
int y=t[x].r;
t[x].r=t[y].l;t[t[y].l].fa=t[y].l?x:0;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;t[y].l=x;t[x].fa=y;
updata(x);updata(y);
}
void rttr(int x)
{
int y=t[x].l;
t[x].l=t[y].r;t[t[y].r].fa=t[y].r?x:0;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;t[y].r=x;t[x].fa=y;
updata(x);updata(y);
}
void splay(int x)
{
if (!x) return;
remove(x);
while (!is_root(x))
{
int p=t[x].fa,g=t[p].fa;
if (is_root(p))
{
if (x==t[p].l) rttr(p);
else rttl(p);
break;
}
if (x==t[p].l)
if (p==t[g].l) rttr(g),rttr(p);
else rttr(p),rttl(g);
else
if (p==t[g].r) rttl(g),rttl(p);
else rttl(p),rttr(g);
}
}
int get_nx(int x)
{
x=t[x].r;
while (t[x].l) x=t[x].l;
splay(x);
return x;
}
int get_fir(int x)
{
while (t[x].l) x=t[x].l;
splay(x);
return x;
}
void access(int x,int w)
{
int y=0;
while (x)
{
splay(x);int nx=get_nx(x);splay(x);
if (!leaf[x]) ans-=!nx?min(t[x].f-1,(t[x].f-b[x])*2):(t[x].f-t[nx].f)*2;
t[x].f+=w;
if (t[nx].f*2<t[x].f+1) t[x].r=0,updata(x);
if (t[y].f*2>=t[x].f+1) t[x].r=y,t[y].fa=x,updata(x);
if (!leaf[x])
if (y&&t[x].r==y) ans+=(t[x].f-t[y].f)*2;
else if (t[x].r) ans+=(t[x].f-t[nx].f)*2;
else ans+=min(t[x].f-1,(t[x].f-a[x])*2);
if (t[x].l) mark(t[x].l,w);
x=get_fir(x);splay(x);y=x;x=t[x].fa;
}
}
void dfs(int x,int fa)
{
f[x]=a[x];LL mx=a[x];leaf[x]=1;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa) continue;
t[e[i].to].fa=x;
dfs(e[i].to,x);
f[x]+=f[e[i].to];
leaf[x]=0;
mx=max(mx,f[e[i].to]);
}
t[x].f=f[x];
ans+=min((f[x]-mx)*2,f[x]-1);
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa&&f[e[i].to]*2>=f[x]+1) t[x].r=e[i].to,updata(x);
}
int main()
{
n=read();m=read();
for (int i=1;i<=n;i++) a[i]=b[i]=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y);
}
for (int i=1;i<=n;i++) t[i].s=1;
dfs(1,0);
printf("%lld\n",ans);
while (m--)
{
int x=read(),w=read();
a[x]+=w;access(x,w);b[x]+=w;
printf("%lld\n",ans);
}
return 0;
}