P4338 [ZJOI2018]历史
作者:互联网
首先可以根据题面描述大致想到这是一道绝对和 \(\text{lct}\) 有关的题。
询问你如何安排操作使得 Access
的复杂度最大。出题人干脆直接让你出数据卡满 \(\text{lct}\) 的复杂度了属于是。
链应该是可以拿的,我们考虑一下不带修。
对于一个节点,我们就考虑这个节点和其子树中关系的虚实情况即可了。
那么显然的,我们令该点的孩子包括该节点中最大的 \(\text{siz}\) 为 \(\text{mx}\) ,该点的 \(\text{siz}\) 为 \(\text{siz}\) ,可得该点的最大贡献为 \(\text{siz}-\text{mx}+\min(\text{siz}-\text{mx},\text{mx}-1)\) ,应该没错吧。
这样的话不带修的应该就有了。
带修的话直接考虑贡献的修改?
考虑 \(\text{lct}\) 上每一个点的实儿子都设为 \(\text{mx}\) 所在的点,然后考虑我们的一次修改实际上就是一个实链上的 \(\text{mx}\) 加,但是对于答案的贡献是略有不同的,存在一个想法是我们同时维护好 \(\text{siz}\) ,用 \(\text{lct}\) 来维护 \(\text{mx}\) 。
一个很严重的问题,这个 \(\min\) 不满足单调性啊,无论是随着修改的单调还是随着深度的单调都不满足。
考虑到最大子树是否大于 \(\text{siz}\) 的二分之一十分重要,我们考虑大于的连实边,否则连虚边。
考虑一个点到根的虚边个数是 \(\log\) 级别的。。。然后直接暴力修改虚边即可,实边是一定还是实边的。
重要:\(\text{lct}\) 真的好难写啊,难受,这里记录一下几个坑点。
- 更新一部分实链的时候不能直接在实链的根上直接打 \(\text{tag}\) 。
- 调取一个位置的值的时候记得 \(\text{clear tag}\) 。
- 将一个位置设为实儿子的时候这个位置一定需要是一棵 \(\text{Splay}\) 的根。
就这样。
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5;
int n,m;long long res=0;bool vis[N];
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Link_Cut_Tree{
struct Node{
int fa,son[2],hv;
long long cnt,mx,siz,res,tag;
}tr[N];
void up(int u){
tr[u].mx=max(tr[u].cnt,tr[tr[u].hv].siz);
tr[u].res=min(tr[u].siz*2-tr[u].mx*2,tr[u].siz-1);
}
void update(int u,long long z){tr[u].siz+=z,tr[u].tag+=z;}
void down(int u){
if(tr[u].son[0]) update(tr[u].son[0],tr[u].tag);
if(tr[u].son[1]) update(tr[u].son[1],tr[u].tag);
tr[u].tag=0;
}
void clear_tag(int u){if(!is_root(u)) clear_tag(tr[u].fa);down(u);}
bool is_root(int u){return tr[tr[u].fa].son[0]!=u&&tr[tr[u].fa].son[1]!=u;}
int get_son(int u){return tr[tr[u].fa].son[1]==u;}
void Rotate(int u){
int fa=tr[u].fa,gfa=tr[fa].fa,k=get_son(u);
if(!is_root(fa)) tr[gfa].son[get_son(fa)]=u;
tr[tr[u].son[k^1]].fa=fa,tr[fa].son[k]=tr[u].son[k^1];
tr[fa].fa=u,tr[u].son[k^1]=fa,tr[u].fa=gfa;
}
void Splay(int u){
clear_tag(u);
for(int fa;fa=tr[u].fa,!is_root(u);Rotate(u))
if(!is_root(fa)) Rotate(get_son(u)==get_son(fa)?fa:u);
}
}t;
void DFS(int u){
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==t.tr[u].fa) continue;
t.tr[v].fa=u,DFS(v),t.tr[u].siz+=t.tr[v].siz;
if(t.tr[t.tr[u].hv].siz<t.tr[v].siz) t.tr[u].hv=v;
}
t.tr[u].siz+=t.tr[u].cnt,t.up(u),res+=t.tr[u].res;
if(t.tr[u].siz+1<=t.tr[t.tr[u].hv].siz*2) t.tr[u].son[1]=t.tr[u].hv;
}
int main(){
// freopen("data.in","r",stdin);
// freopen("shit.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;++i) scanf("%lld",&t.tr[i].cnt);
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
DFS(1),printf("%lld\n",res);
for(int i=1;i<=m;++i){
int u,v=0,w;scanf("%d%d",&u,&w),t.tr[u].cnt+=w;
while(u){
if(t.tr[u].hv) t.Splay(t.tr[u].hv);
t.Splay(u),t.tr[u].siz+=w;
if(t.tr[u].son[0]) t.update(t.tr[u].son[0],w);
if(t.tr[v].siz>t.tr[t.tr[u].hv].siz) t.tr[u].hv=v;
res-=t.tr[u].res,t.up(u),res+=t.tr[u].res;
if(t.tr[u].siz+1<=t.tr[t.tr[u].hv].siz*2) t.tr[u].son[1]=t.tr[u].hv;
else t.tr[u].son[1]=0;
while(t.tr[u].son[0]){u=t.tr[u].son[0];}
t.Splay(u),v=u,u=t.tr[u].fa;
}
printf("%lld\n",res);
}
return 0;
}
标签:历史,res,text,P4338,tr,lct,siz,ZJOI2018,mx 来源: https://www.cnblogs.com/Point-King/p/15800579.html