[ZJOI2015] 幻想乡战略游戏——树链剖分

[ZJOI2015]幻想乡战略游戏

题解

由于所有边的边权是正整数,所以可以发现任何时刻每个点的答案是从最优的点往周围递增的。如果有平台,那么一定是在最优点的连通块那儿。

我们先考虑如何快速求每个点的答案。把式子拆开来:
f u = ∑ v = 1 n ( d v ∗ d i s u , v ) = ∑ v = 1 n ( d v ∗ ( d e p u + d e p v − 2 ∗ d e p l c a ( u , v ) ) ) = d e p u ∗ ( ∑ v = 1 n d v ) + ∑ v = 1 n ( d v ∗ d e p v ) − ∑ v = 1 n ( d v ∗ 2 ∗ d e p l c a ( u , v ) ) f_u=\sum_{v=1}^n(d_v*dis_{u,v})\\ =\sum_{v=1}^n(d_v*(dep_u+dep_v-2*dep_{lca(u,v)}))\\ =dep_u*(\sum_{v=1}^nd_v)+\sum_{v=1}^n(d_v*dep_v)-\sum_{v=1}^n(d_v*2*dep_{lca(u,v)}) fu=v=1n(dvdisu,v)=v=1n(dv(depu+depv2deplca(u,v)))=depu(v=1ndv)+v=1n(dvdepv)v=1n(dv2deplca(u,v))可以发现前两部分都是可以 O ( 1 ) O(1) O(1) 求的,第三部分我们可以用经典思路,把每个点的点权设为它的父亲边的长度,然后用树链剖分,每次修改把 x x x 到根的一条链的值全部减去2*点权,然后查询时只用求出到根的链上的值的和。

查询答案的问题解决了,剩下就可以直接用树剖在树上找最优点了。我们从任意一个点开始,它所在的重链的答案一定是单峰的,并且非峰顶的部分无平台,所以我们可以每条重链用平衡树维护之前的答案,然后在平衡树上二分找到峰顶。接下来全树最优点如果不是这个峰顶的话,必定要从这个峰顶走出这条重链去找,我们可以直接枚举与它相邻的点,那么此时最多有一个点的答案更优,走那个方向递归下去即可。

查询答案可以用全局平衡二叉树做到 O ( log ⁡ n ) O(\log n) O(logn)。这个做法毛估一下大概是 O ( n log ⁡ 3 n ) O(n\log^3n) O(nlog3n) 的,但是却跑得出奇地快(直接进入 O ( n log ⁡ n ) ∼ O ( n log ⁡ 2 n ) O(n\log n)\sim O(n\log^2 n) O(nlogn)O(nlog2n) 梯队),大概是因为其中的两个 log ⁡ \log log 都跑不满的缘故。

代码

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=100005;
const ll INF=1e18;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}

struct edge{
	int v,to;ll w;edge(){}
	edge(int V,int T,ll W){v=V,to=T,w=W;}
}e[MAXN<<1];
int EN,G[MAXN];
inline void addedge(int u,int v,ll w){
	e[++EN]=edge(v,G[u],w),G[u]=EN;
	e[++EN]=edge(u,G[v],w),G[v]=EN;
}
int n,m,siz[MAXN],fa[MAXN],hs[MAXN];
ll dep[MAXN],d[MAXN],sd,sdv;
int hd[MAXN],id[MAXN],IN,tl[MAXN],tp[MAXN],rt[MAXN];
struct itn{
	int ls,rs;
	ll a,s,g,f,lz;itn(){}
	itn(ll A){a=s=A,g=f=lz=0,ls=rs=0;}
}t[MAXN];
inline void cover(int x,ll z){
	if(!x)return;
	t[x].f+=t[x].s*z,t[x].g+=t[x].a*z,t[x].lz+=z;
}
inline void pushd(int x){
	if(t[x].lz)
		cover(t[x].ls,t[x].lz),cover(t[x].rs,t[x].lz),t[x].lz=0;
}
inline void update(int x){
	t[x].s=t[x].a+t[t[x].ls].s+t[t[x].rs].s;
	t[x].f=t[x].g+t[t[x].ls].f+t[t[x].rs].f;
}
inline void add(int x,int l,int r,int a,int b,ll d){
	if(!x||l>r)return;
	if(l>=a&&r<=b){cover(x,d);return;}
	pushd(x);
	if(x>=a&&x<=b)t[x].g+=t[x].a*d;
	if(a<x)add(t[x].ls,l,x-1,a,b,d);
	if(b>x)add(t[x].rs,x+1,r,a,b,d);
	update(x);
}
inline ll query(int x,int l,int r,int a,int b){
	if(!x||l>r)return 0;
	if(l>=a&&r<=b)return t[x].f;
	ll res=(x>=a&&x<=b?t[x].g:0);pushd(x);
	if(a<x)res+=query(t[x].ls,l,x-1,a,b);
	if(b>x)res+=query(t[x].rs,x+1,r,a,b);
	return res;
}

inline int build(int l,int r){
	if(l>r)return 0;
	int p=0,s=0,x=l;
	for(int i=l;i<=r;i++)s+=siz[id[i]]-siz[hs[id[i]]];
	for(;x<r;x++){
		p+=siz[id[x]]-siz[hs[id[x]]];
		if((p<<1)>s)break;
	}t[x]=itn(dep[id[x]]-dep[fa[id[x]]]);
	t[x].ls=build(l,x-1),t[x].rs=build(x+1,r);
	return update(x),x;
}
inline void dfs1(int x){
	siz[x]=1,hs[x]=0;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(v==fa[x])continue;
		fa[v]=x,dep[v]=dep[x]+e[i].w;
		dfs1(v),siz[x]+=siz[v];
		if(siz[v]>siz[hs[x]])hs[x]=v;
	}
}
inline void dfs2(int x){
	hd[x]=++IN,id[IN]=x,tp[x]=(x==hs[fa[x]]?tp[fa[x]]:x),tl[tp[x]]=IN;
	if(hs[x])dfs2(hs[x]);
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(v==fa[x]||v==hs[x])continue;
		dfs2(v);
	}if(x==tp[x])rt[x]=build(hd[x],tl[x]);
}
inline void addl(int x,ll z){
	while(x)add(rt[tp[x]],hd[tp[x]],tl[tp[x]],hd[tp[x]],hd[x],z),x=fa[tp[x]];
}
inline ll schl(int x){
	ll res=0;
	while(x)res+=query(rt[tp[x]],hd[tp[x]],tl[tp[x]],hd[tp[x]],hd[x]),x=fa[tp[x]];
	return res;
}
inline ll getv(int x){return dep[x]*sd+sdv+schl(x);}
#define pli pair<ll,int>
#define fi first
#define se second
inline pli schmn(int x,int l,int r,ll pr){
	if(!x||l>r)return pli(INF,0);
	ll pv=INF;pushd(x);
	pli res=pli(pr+t[t[x].ls].f+t[x].g+dep[id[x]]*sd+sdv,id[x]);
	if(fa[id[x]])pv=pr+t[t[x].ls].f+dep[fa[id[x]]]*sd+sdv;
	if(pv<=res.fi)res=min(res,schmn(t[x].ls,l,x-1,pr));
	else res=min(res,schmn(t[x].rs,x+1,r,pr+t[t[x].ls].f+t[x].g));
	return res;
}
inline pli findp(int x){
	pli as=schmn(rt[tp[x]],hd[tp[x]],tl[tp[x]],schl(fa[tp[x]]));
	x=as.se;
	for(int i=G[x];i;i=e[i].to){
		int v=e[i].v;
		if(tp[v]==tp[x])continue;
		ll val=getv(v);
		if(val<as.fi)as=pli(val,v);
	}
	if(as.se^x)return findp(as.se);
	else return as;
}
signed main()
{
	n=read(),m=read();
	for(int i=1,u,v;i<n;i++)
		u=read(),v=read(),addedge(u,v,read());
	dfs1(1),dfs2(1);
	for(int i=1;i<=m;i++){
		int x=read(),e=read();
		sd+=e,sdv+=e*dep[x],addl(x,-(e<<1));
		print(findp(1).fi);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值