其他分享
首页 > 其他分享> > P4338 [ZJOI2018]历史

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}\) 真的好难写啊,难受,这里记录一下几个坑点。

  1. 更新一部分实链的时候不能直接在实链的根上直接打 \(\text{tag}\) 。
  2. 调取一个位置的值的时候记得 \(\text{clear tag}\) 。
  3. 将一个位置设为实儿子的时候这个位置一定需要是一棵 \(\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