[SDOI2016] 游戏

这是一篇关于SDOI2016游戏中涉及的一次函数路径问题的解析。通过分析,发现可以利用李超树来维护路径上的特定函数表达式。文章详细介绍了如何将问题转化为树链剖分结合李超树的区间查询,并处理特殊情况,以实现O(nlog^3n)的时间复杂度解题策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目

点此看题

二、解法

你看那个 a × d i s + b a\times dis+b a×dis+b 很像一次函数,可以用李超树维护。

这个 d i s dis dis 还是太抽象了,我们要把具体的函数表达式推出来才能做:

  • 对于 ( u , l c a ) (u,lca) (u,lca) 这一条路径上的点,有: a × ( d i s [ u ] − d i s [ x ] ) + b = − a × d i s [ u ] + ( a d i s [ x ] + b ) a\times(dis[u]-dis[x])+b=-a\times dis[u]+(adis[x]+b) a×(dis[u]dis[x])+b=a×dis[u]+(adis[x]+b)
  • 对于 ( v , l c a ) (v,lca) (v,lca) 这一条路径上的点,有: a × ( d i s [ x ] + d i s [ u ] − 2 d i s [ l c a ] ) + b = a × d i s [ x ] + a ( d i s [ u ] − d i s [ l c a ] ) + b a\times(dis[x]+dis[u]-2dis[lca])+b=a\times dis[x]+a(dis[u]-dis[lca])+b a×(dis[x]+dis[u]2dis[lca])+b=a×dis[x]+a(dis[u]dis[lca])+b

那么用树链剖分套李超树即可,这道题很特殊的一点李超树是区间查询的。

怎么办?我们维护一个 m n [ i ] mn[i] mn[i] 表示节点 i i i 管辖的范围的最小值,在 u p d a t a \tt updata updata 的时候 p u s h _ u p \tt push\_up push_up 维护就可以了,只用算左右端点处的值。如果区间没有完全覆盖,那么就查询 max ⁡ ( l , L ) \max(l,L) max(l,L) min ⁡ ( r , R ) \min(r,R) min(r,R) 处的点值,其实把他当成标记永久化就很好理解了。

时间复杂度 O ( n log ⁡ 3 n ) O(n\log^3n) O(nlog3n),难打,我们常数还是小啊, l u o g u \tt luogu luogu 直接 r a n k 4 \tt rank4 rank4

#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
const int M = 100005;
const ll inf = 123456789123456789ll;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,tot,f[M],dep[M],fa[M],son[M],siz[M];
int Index,num[M],top[M],id[M];ll mn[4*M],dis[M];
struct edge
{
	int v,c,next;
	edge(int V=0,int C=0,int N=0) : v(V) , c(C) , next(N) {}
}e[2*M];
struct node
{
	ll k,b;
	node(ll K=0,ll B=inf) : k(K) , b(B) {}
	ll ask(ll x)
	{
		return k*dis[id[x]]+b;
	}
}tr[4*M];
void dfs1(int u,int p)
{
	fa[u]=p;siz[u]=1;
	dep[u]=dep[p]+1;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v,c=e[i].c;
		if(v==p) continue;
		dis[v]=dis[u]+c;
		dfs1(v,u);
		siz[u]+=siz[v];//忘打了 
		if(siz[son[u]]<siz[v])
			son[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;
	num[u]=++Index;
	id[Index]=u;
	if(son[u]) dfs2(son[u],tp);
	for(int i=f[u];i;i=e[i].next)
		if(e[i].v^fa[u] && e[i].v^son[u])
			dfs2(e[i].v,e[i].v);
}
int lca(int u,int v)
{
	while(top[u]^top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	return u;
}
void up(int i)
{
	mn[i]=min(mn[i],min(mn[i<<1],mn[i<<1|1]));
}
void ins(int i,int l,int r,int L,int R,node b)
{
	if(L>r || l>R) return ;
	int mid=(l+r)>>1;
	if(L<=l && r<=R)
	{
		if(tr[i].ask(mid)>b.ask(mid))//b更优,下传tr[i] 
		{
			if(tr[i].ask(l)<b.ask(l)) ins(i<<1,l,mid,L,R,tr[i]);
			if(tr[i].ask(r)<b.ask(r)) ins(i<<1|1,mid+1,r,L,R,tr[i]);
			tr[i]=b;
		}
		else
		{
			if(tr[i].ask(l)>b.ask(l)) ins(i<<1,l,mid,L,R,b);
			if(tr[i].ask(r)>b.ask(r)) ins(i<<1|1,mid+1,r,L,R,b);
		}
		mn[i]=min(tr[i].ask(l),tr[i].ask(r));
		if(l!=r) up(i);//NMB,一个臭b错误调nm一天 
		return ;
	}
	ins(i<<1,l,mid,L,R,b);
	ins(i<<1|1,mid+1,r,L,R,b);
	up(i);
}
ll ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return inf;
	if(L<=l && r<=R) return mn[i];
	int mid=(l+r)>>1;ll Mn=min(tr[i].ask(max(l,L)),tr[i].ask(min(r,R)));
	return min(Mn,min(ask(i<<1,l,mid,L,R),ask(i<<1|1,mid+1,r,L,R)));
}
void modify(int u,int v)
{
	int a=read(),b=read();
	node y1=node(-a,1ll*a*dis[u]+b);
	node y2=node(a,a*(dis[u]-2*dis[lca(u,v)])+b);
	while(top[u]^top[v])
	{
		if(dep[top[u]]>dep[top[v]])
		{
			ins(1,1,n,num[top[u]],num[u],y1);
			u=fa[top[u]];
		}
		else
		{
			ins(1,1,n,num[top[v]],num[v],y2);
			v=fa[top[v]];
		}
	}
	if(dep[u]>dep[v]) ins(1,1,n,num[v],num[u],y1);
	else ins(1,1,n,num[u],num[v],y2);
}
ll query(int u,int v)
{
	ll res=inf;
	while(top[u]^top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		res=min(res,ask(1,1,n,num[top[u]],num[u]));
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	res=min(res,ask(1,1,n,num[u],num[v]));
	return res;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge(v,c,f[u]),f[u]=tot;
		e[++tot]=edge(u,c,f[v]),f[v]=tot;
	}
	dfs1(1,0);
	dfs2(1,0);
	for(int i=1;i<4*M;i++) mn[i]=inf;
	while(m--)
	{
		int op=read(),s=read(),t=read();
		if(op==1) modify(s,t);
		else printf("%lld\n",query(s,t));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值