树链剖分 专练

260 阅读8分钟

月下毛景树

题目链接

思路

树链剖分裸题,但是维护的是边权,考虑到每个节点仅有一个父亲,因此用节点来维护与它父亲相连的那条边的边权。

第一种操作,记录下每条边深度最深的端点,单点修改即可;
第二种操作,沿着重链区间覆盖;
第三种操作,沿着重链区间加;
第四种操作,沿着重链查区间最大值。

用线段树来维护这些信息即可。另外要注意两点的lca是不参与路径上的修改的,因此每次修改时要保证lca的点权不变。

代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,tot,timer;
ll siz[N],father[N],dep[N],head[N],son[N],dfsn[N],top[N],maxi[4*N],tag[4*N],fg[4*N];
struct edge{
	ll u,v,w;
}edge[N];
struct Edge{
	ll nxt,to,w;
}e[2*N];
void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot]=(Edge){head[u],v,w};
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
void dfs(ll u,ll fa)
{
	siz[u]=1;
	father[u]=fa;
	dep[u]=dep[fa]+1;
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==fa)
			continue;
		dfs(v,u);
		if(siz[v]>siz[son[u]])
			son[u]=v;
		siz[u]+=siz[v];
	}
}
void dfs_(ll u,ll tp)
{
	dfsn[u]=++timer;
	top[u]=tp;
	if(!son[u])
		return;
	dfs_(son[u],tp);
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==father[u] || v==son[u])
			continue;
		dfs_(v,v);
	}
}
ll lca(ll u,ll v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		u=father[top[u]];
	}
	return dep[u]<=dep[v]?u:v;
}
void update(ll p)
{
	maxi[p]=max(maxi[lc],maxi[rc]);
}
void flag(ll p,ll add_val,ll cover_val)
{
	if(cover_val!=-1)
	{
		maxi[p]=fg[p]=cover_val;
		tag[p]=0;
	}
	if(add_val)
	{
		maxi[p]+=add_val;
		tag[p]+=add_val;
	}
}
void push_down(ll p)
{
	flag(lc,tag[p],fg[p]);
	flag(rc,tag[p],fg[p]);
	tag[p]=0;
	fg[p]=-1;
}
void modify(ll p,ll l,ll r,ll al,ll ar,ll val)
{
	if(al<=l && ar>=r)
	{
		maxi[p]+=val;
		tag[p]+=val;
		return;
	}
	ll mid=(l+r)>>1;
	push_down(p);
	if(al<=mid)
		modify(lc,l,mid,al,ar,val);
	if(ar>mid)
		modify(rc,mid+1,r,al,ar,val);
	update(p);
}
void cover(ll p,ll l,ll r,ll al,ll ar,ll val)
{
	if(al<=l && ar>=r)
	{
		maxi[p]=fg[p]=val;
		tag[p]=0;
		return;
	}
	push_down(p);
	ll mid=(l+r)>>1;
	if(al<=mid)
		cover(lc,l,mid,al,ar,val);
	if(ar>mid)
		cover(rc,mid+1,r,al,ar,val);
	update(p);
}
ll query(ll p,ll l,ll r,ll al,ll ar)
{
	if(al<=l && ar>=r)
		return maxi[p];
	ll mid=(l+r)>>1,ret=0;
	push_down(p);
	if(al<=mid)
		ret=max(ret,query(lc,l,mid,al,ar));
	if(ar>mid)
		ret=max(ret,query(rc,mid+1,r,al,ar));
	return ret;
}
void print()
{
	rep(i,1,7)
	{
		op(maxi[i],tag[i],fg[i]);
		en;
	}
	en;
}
int main()
{
	read(n);
	ll u,v,w;
	memset(fg,-1,sizeof(fg));
	rep(i,1,n-1)
	{
		read(edge[i].u,edge[i].v,edge[i].w);
		add_edge(edge[i].u,edge[i].v,0,1);
	}
	dfs(1,0);
	dfs_(1,1);
	rep(i,1,n-1)
	{
		ll &u=edge[i].u,&v=edge[i].v;
		if(dep[u]<dep[v])
			swap(u,v);
		modify(1,1,n,dfsn[u],dfsn[u],edge[i].w);
	}
	string s;
	while(cin>>s)
	{
		if(s=="Stop")
			break;
		read(u,v);
		if(s=="Change")
		{
			u=edge[u].u;
			cover(1,1,n,dfsn[u],dfsn[u],v);
		}
		else if(s=="Cover")
		{
			read(w);
			ll p=lca(u,v),tmp=query(1,1,n,dfsn[p],dfsn[p]);
			while(top[u]!=top[v])
			{
				if(dep[top[u]]<dep[top[v]])
					swap(u,v);
				cover(1,1,n,dfsn[top[u]],dfsn[u],w);
				u=father[top[u]];
			}
			if(dep[u]>dep[v])
				swap(u,v);
			cover(1,1,n,dfsn[u],dfsn[v],w);
			cover(1,1,n,dfsn[p],dfsn[p],tmp);
		}
		else if(s=="Add")
		{
			read(w);
			ll p=lca(u,v);
			while(top[u]!=top[v])
			{
				if(dep[top[u]]<dep[top[v]])
					swap(u,v);
				modify(1,1,n,dfsn[top[u]],dfsn[u],w);
				u=father[top[u]];
			}
			if(dep[u]>dep[v])
				swap(u,v);
			modify(1,1,n,dfsn[u],dfsn[v],w);
			modify(1,1,n,dfsn[p],dfsn[p],-w);
		}
		else if(s=="Max")
		{
			ll p=lca(u,v),tmp=query(1,1,n,dfsn[p],dfsn[p]),ans=0;
			cover(1,1,n,dfsn[p],dfsn[p],0);
			while(top[u]!=top[v])
			{
				if(dep[top[u]]<dep[top[v]])
					swap(u,v);
				ans=max(ans,query(1,1,n,dfsn[top[u]],dfsn[u]));
				u=father[top[u]];
			}
			if(dep[u]>dep[v])
				swap(u,v);
			ans=max(ans,query(1,1,n,dfsn[u],dfsn[v]));
			OP(ans);
			modify(1,1,n,dfsn[p],dfsn[p],tmp);
		}
	}
}

Disruption P

题目链接

思路

考虑每一条额外的边,添加这条边之后,就可以删去其两个端点间在原树路径上的任意一条边,因此可以用这条额外边的权值去更新这条路径上所有边的答案,用树链剖分维护一下即可。

还有一种更妙的做法,权值小的额外边更新路径上的答案后,这些答案就不需要再被更新了,因此像并查集那样维护一下每个原树上已经被更新过的边,直接往上跳就行。

树链剖分代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=5E4+10;
ll n,m,tot,timer;
ll head[N],dep[N],siz[N],father[N],top[N],dfsn[N],son[N],mini[4*N],tag[4*N];
struct edge{
	ll u,v;
}edge[N];
struct Edge{
	ll nxt,to,w;
}e[N<<1];
void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot]=(Edge){head[u],v,w};
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
void dfs(ll u,ll fa)
{
	dep[u]=dep[fa]+1;
	siz[u]=1;
	father[u]=fa;
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==fa)
			continue;
		dfs(v,u);
		if(siz[v]>siz[son[u]])
			son[u]=v;
		siz[u]+=siz[v];
	}
}
void dfs_(ll u,ll tp)
{
	top[u]=tp;
	dfsn[u]=++timer;
	if(!son[u])
		return;
	dfs_(son[u],tp);
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==son[u] || v==father[u])
			continue;
		dfs_(v,v);
	}
}
void update(ll p)
{
	mini[p]=min(mini[lc],mini[rc]);
}
void push_down(ll p)
{
	tag[lc]=min(tag[lc],tag[p]);
	tag[rc]=min(tag[rc],tag[p]);
	mini[lc]=min(mini[lc],tag[p]);
	mini[rc]=min(mini[rc],tag[p]);
	tag[p]=LLM;
}
void cover(ll p,ll l,ll r,ll al,ll ar,ll val,ll ty)
{
	if(al<=l && ar>=r)
	{
		if(ty)
		{
			mini[p]=val;
			return;
		}
		mini[p]=min(mini[p],val);
		tag[p]=min(tag[p],val);
		return;
	}
	ll mid=(l+r)>>1;
	push_down(p);
	if(al<=mid)
		cover(lc,l,mid,al,ar,val,ty);
	if(ar>mid)
		cover(rc,mid+1,r,al,ar,val,ty);
	update(p);
}
ll query(ll p,ll l,ll r,ll al,ll ar)
{
	if(al<=l && ar>=r)
		return mini[p];
	ll mid=(l+r)>>1,ret=LLM;
	push_down(p);
	if(al<=mid)
		ret=query(lc,l,mid,al,ar);
	if(ar>mid)
		ret=min(ret,query(rc,mid+1,r,al,ar));
	return ret;
}
ll lca(ll u,ll v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		u=father[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
int main()
{
	read(n,m);
	rep(i,1,n-1)
	{
		read(edge[i].u,edge[i].v);
		add_edge(edge[i].u,edge[i].v,0,1);
	}
	dfs(1,0);
	dfs_(1,1);
	rep(i,1,4*n)
		mini[i]=tag[i]=LLM;
	ll u,v,w;
	rep(i,1,m)
	{
		read(u,v,w);
		ll p=lca(u,v),tmp=query(1,1,n,dfsn[p],dfsn[p]);
		while(top[u]!=top[v])
		{
			if(dep[top[u]]<dep[top[v]])
				swap(u,v);
			cover(1,1,n,dfsn[top[u]],dfsn[u],w,0);
			u=father[top[u]];
		}
		if(dep[u]>dep[v])
			swap(u,v);
		cover(1,1,n,dfsn[u],dfsn[v],w,0);
		cover(1,1,n,dfsn[p],dfsn[p],tmp,1);
	}
	rep(i,1,n-1)
	{
		if(dep[edge[i].u]<dep[edge[i].v])
			swap(edge[i].u,edge[i].v);
		ll res=query(1,1,n,dfsn[edge[i].u],dfsn[edge[i].u]);
		if(res==LLM)
			res=-1;
		OP(res);
	}
}

并查集代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=5E4+10;
ll n,m,tot;
ll dep[N],f[N][20],fa[N],ans[N],head[N];
struct edge
{
	ll u,v,w;
}medge[N],eedge[N];
struct Edge{
	ll nxt,to,w;
}e[2*N];
void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot]=(Edge){head[u],v,w};
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
void dfs(ll u,ll fa)
{
	dep[u]=dep[fa]+1;
	rep(i,0,18)
		f[u][i+1]=f[f[u][i]][i];
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==fa)
			continue;
		f[v][0]=u;
		dfs(v,u);
	}
}
ll lca(ll u,ll v)
{
	if(dep[u]<dep[v])
		swap(u,v);
	for(int i=18;i>=0;--i)
	{
		if(dep[f[u][i]]>=dep[v])
			u=f[u][i];
		if(u==v)
			return v;
	}
	for(int i=18;i>=0;--i)
	{
		if(f[u][i]!=f[v][i])
		{
			u=f[u][i];
			v=f[v][i];
		}
	}
	return f[u][0];
}
ll find(ll x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
	read(n,m);
	rep(i,1,n)
		fa[i]=i;
	ll u,v,w;
	rep(i,1,n-1)
	{
		read(u,v);
		add_edge(u,v,0,1);
		eedge[i]=(edge){u,v};
	}
	rep(i,1,m)
	{
		read(u,v,w);
		medge[i]=(edge){u,v,w};
	}
	sort(medge+1,medge+m+1,[](const edge &u,const edge &v)
	{
		return u.w<v.w;
	});
	memset(ans,-1,sizeof(ans));
	dfs(1,0);
	rep(i,1,n)
	{
		ll &u=eedge[i].u,&v=eedge[i].v;
		if(dep[u]<dep[v])
			swap(u,v);
	}
	rep(i,1,m)
	{
		ll u=medge[i].u,v=medge[i].v,w=medge[i].w,p=lca(u,v);
		while(dep[u]>dep[p])
		{
			ll fu=find(u);
			if(fu!=u)
				u=fu;
			else
			{
				ans[u]=w;
				fa[u]=f[u][0];
				u=f[u][0];
			}
		}
		while(dep[v]>dep[p])
		{
			ll fv=find(v);
			if(fv!=v)
				v=fv;
			else
			{
				ans[v]=w;
				fa[v]=f[v][0];
				v=f[v][0];
			}
		}
	}
	rep(i,1,n-1)
	{
		OP(ans[eedge[i].u]);
	}
}

I love tree

题目链接

思路

难度在于维护区间加平方,区间内每个位置加的数都各不相同。因此要找出它们的共同点。考虑对[L,R][L,R]从左到右加递增的数字,LL位置加了valval,那么下标为ii的位置就加了(iL+val)2(i-L+val)^2。设l=Lvall=L-val,则原式可以写成(il)2=i2+l22il(i-l)^2=i^2+l^2-2il。因为ii是一直不变的,因此只维护这个区间被加了多少次,l2l^2的总和以及2l-2l的总和即可,单点询问时直接加上该点ii值的贡献。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,q,tot,timer;
ll head[N],dfsn[N],dep[N],son[N],father[N],siz[N],top[N];
struct Edge{
	ll nxt,to,w;
}e[2*N];
void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot]=(Edge){head[u],v,w};
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
struct BIT
{
	ll t[N];
	void add(ll x,ll val)
	{
		while(x<=n)
		{
			t[x]+=val;
			x+=x&-x;
		}
	}
	ll query(ll x)
	{
		ll ret=0;
		while(x)
		{
			ret+=t[x];
			x-=x&-x;
		}
		return ret;
	}
	void modify(ll L,ll R,ll val)
	{
		add(L,val);
		add(R+1,-val);
	}
}T1,T2,T3;
ll lca(ll u,ll v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])
			swap(u,v);
		u=father[top[u]];
	}
	return dep[u]>dep[v]?v:u;
}
ll dis(ll u,ll v)
{
	ll p=lca(u,v);
	return dep[u]+dep[v]-2*dep[p];
}
void dfs(ll u,ll fa)
{
	dep[u]=dep[fa]+1;
	siz[u]=1;
	father[u]=fa;
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==fa)
			continue;
		dfs(v,u);
		if(siz[v]>siz[son[u]])
			son[u]=v;
		siz[u]+=siz[v];
	}
}
void dfs_(ll u,ll tp)
{
	top[u]=tp;
	dfsn[u]=++timer;
	if(!son[u])
		return;
	dfs_(son[u],tp);
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==son[u] || v==father[u])
			continue;
		dfs_(v,v);
	}
}
ll gg(ll u)
{
	ll du=dfsn[u];
	return T1.query(du)+T2.query(du)*du*du-T3.query(du)*du;
}
int main()
{
	read(n);
	ll ty,u,v;
	rep(i,1,n-1)
	{
		read(u,v);
		add_edge(u,v,0,1);
	}
	dfs(1,0);
	dfs_(1,1);
	read(q);
	while(q--)
	{
		read(ty);
		if(ty==1)
		{
			read(u,v);
			ll st=u,p=lca(u,v),midis=dis(u,p);
			while(top[u]!=top[v])
			{
				if(dep[top[u]]<dep[top[v]])
					swap(u,v);
				ll val=dis(st,top[u])+1;
				ll tmp=dfsn[top[u]]-val;
				if(dis(u,st)<midis)
					tmp=dfsn[top[u]]+val;
				T1.modify(dfsn[top[u]],dfsn[u],tmp*tmp);
				T2.modify(dfsn[top[u]],dfsn[u],1);
				T3.modify(dfsn[top[u]],dfsn[u],2*tmp);
				u=father[top[u]];
			}
			if(dep[u]>dep[v])
				swap(u,v);
			ll val=dis(st,u)+1;
			ll tmp=dfsn[u]-val;
			if(dis(v,st)<midis)
				tmp=dfsn[u]+val;
			T1.modify(dfsn[u],dfsn[v],tmp*tmp);
			T2.modify(dfsn[u],dfsn[v],1);
			T3.modify(dfsn[u],dfsn[v],2*tmp);
			// op(T1.query(2),T2.query(2),T3.query(2),gg(2));
			// en;
		}
		else
		{
			read(u);
			OP(gg(u));
		}
	}
}