【BZOJ2566】xmastree(点分树)

传送门


题解:

可能手残到了一定程度才会像我一样在算子树siz的时候将siz[u]+=siz[v]打成siz[v]+=siz[u],然后莫名其妙挂一堆点

思路挺简单,码起来没什么细节就是有点长~

建立点分树,每个分治中心维护两个东西,各个子树中每种颜色到自己的最小距离的序列,自己掌管的子树中每种颜色的点到分治父亲的距离的序列。

显然自己更新答案就是考虑每种颜色将两个来自不同子树的路径拼起来,然后用自己子树中每个颜色到分治父亲的最小值来更新父亲的集合。

所有集合可以用multiset维护。

预处理 r m q rmq rmq之后可以 O ( 1 ) O(1) O(1)查询树上距离,复杂度 O ( n log ⁡ 2 n + m log ⁡ 2 n ) O(n\log^2n+m\log^2n) O(nlog2n+mlog2n)


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
#define fi first
#define se second

cs int N=1.2e4+7,INF=0x3f3f3f3f;

int n,m;

int el[N],nxt[N<<1],to[N<<1],w[N<<1],ec;
inline void adde(int u,int v,int val){
	nxt[++ec]=el[u],el[u]=ec,to[ec]=v,w[ec]=val;
	nxt[++ec]=el[v],el[v]=ec,to[ec]=u,w[ec]=val;
}

namespace ST{
	int in[N],Log[N<<1],dfn;ll d[N],mn[17][N<<1];
	void dfs(int u,int p){
		in[u]=++dfn;mn[0][dfn]=d[u];
		for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
		if(v!=p){d[v]=d[u]+w[e];dfs(v,u);mn[0][++dfn]=d[u];}
	}
	inline void init(){
		dfs(1,0);for(int re i=2;i<=dfn;++i)Log[i]=Log[i>>1]+1;
		for(int re i=1;i<=Log[dfn];++i)
		for(int re j=1;j+(1<<i)-1<=dfn;++j)
		mn[i][j]=std::min(mn[i-1][j],mn[i-1][j+(1<<i-1)]);
	}
	inline ll dis(int u,int v){
		int l=in[u],r=in[v];if(l>r)std::swap(l,r);
		int t=Log[r-l+1];return d[u]+d[v]-2*std::min(mn[t][l],mn[t][r-(1<<t)+1]);
	}
}
using ST::dis;

std::multiset<ll> A;
std::map<int,std::multiset<ll> > a[N],b[N];
inline ll get_ans(cs std::multiset<ll> &s){return *(s.begin())+*(++s.begin());}

int col[N];
int fa[N],ban[N],siz[N];
int rt,total,mx_siz;

void get_siz(int u,int p){
	siz[u]=1;for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p&&!ban[v]){get_siz(v,u);siz[u]+=siz[v];}
}
void find_rt(int u,int p){
	int mx=total-siz[u];
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p&&!ban[v]){find_rt(v,u);mx=std::max(mx,siz[v]);}
	if(mx<mx_siz){mx_siz=mx,rt=u;}
}
void get_rt(int u){
	get_siz(u,0);
	total=siz[u],mx_siz=INF;
	find_rt(u,0);
}

void get_b(int u,int p){
	b[rt][col[u]].insert(dis(u,fa[rt]));
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(v!=p&&!ban[v])get_b(v,u);
}

void DFS(int u){
	ban[u]=true;a[u][col[u]].insert(0ll);
	if(fa[u])get_b(u,0);
	for(int re e=el[u],v=to[e];e;v=to[e=nxt[e]])
	if(!ban[v]){
		get_rt(v);fa[rt]=u;
		v=rt;DFS(rt);
		for(auto cs &ad:b[v])if(ad.se.size()>0)
		a[u][ad.fi].insert(*ad.se.begin());
	}
	for(auto cs &ad:a[u])if(ad.se.size()>1)
	A.insert(get_ans(ad.se));
}

void ins(int u,int c){
	ll ans1=-1,ans2=-1;
	if(a[u][c].size()>1)ans1=get_ans(a[u][c]);
	a[u][c].insert(0);
	if(a[u][c].size()>1)ans2=get_ans(a[u][c]);
	if(ans1!=ans2){
		if(~ans1)A.erase(A.find(ans1));
		A.insert(ans2);
	}
	for(int re v=u,p=fa[v];p;p=fa[v=p]){
		ans1=ans2=-1;ll t1=-1,t2=-1;
		if(a[p][c].size()>1)ans1=get_ans(a[p][c]);
		if(b[v][c].size())t1=*b[v][c].begin();
		b[v][c].insert(dis(u,p));t2=*b[v][c].begin();
		if(t1!=t2){
			if(~t1)a[p][c].erase(a[p][c].find(t1));
			a[p][c].insert(t2);
		}
		if(a[p][c].size()>1)ans2=get_ans(a[p][c]);
		if(ans1!=ans2){
			if(~ans1)A.erase(A.find(ans1));
			A.insert(ans2);
		}
	}
}

void del(int u,int c){
	ll ans1=-1,ans2=-1;
	if(a[u][c].size()>1)ans1=get_ans(a[u][c]);
	a[u][c].erase(a[u][c].find(0));
	if(a[u][c].size()>1)ans2=get_ans(a[u][c]);
	if(ans1!=ans2){
		A.erase(A.find(ans1));
		if(~ans2)A.insert(ans2);
	}
	for(int re v=u,p=fa[v];p;p=fa[v=p]){
		ans1=ans2=-1;ll t1=-1,t2=-1;
		if(a[p][c].size()>1)ans1=get_ans(a[p][c]);
		t1=*b[v][c].begin();b[v][c].erase(b[v][c].find(dis(u,p)));
		if(b[v][c].size())t2=*b[v][c].begin();
		if(t1!=t2){
			a[p][c].erase(a[p][c].find(t1));
			if(~t2)a[p][c].insert(t2);
		}
		if(a[p][c].size()>1)ans2=get_ans(a[p][c]);
		if(ans1!=ans2){
			A.erase(A.find(ans1));
			if(~ans2)A.insert(ans2);
		}
	}
}

signed main(){
#ifdef zxyoi
	freopen("xmastree.in","r",stdin);
#endif
	n=gi();
	for(int re i=1;i<=n;++i)col[i]=gi();
	for(int re i=1;i<n;++i){int u=gi(),v=gi(),val=gi();adde(u,v,val);}
	ST::init();get_rt(1);DFS(rt);m=gi();
	cout<<(A.size()?*A.begin():-1)<<"\n";
	while(m--){
		int u=gi(),c=gi();
		if(col[u]!=c)del(u,col[u]),ins(u,col[u]=c);
		cout<<(A.size()?*A.begin():-1)<<"\n";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值