【LuoguP4069】 [SDOI2016]游戏

题目链接

题意

树上路径插入以到起点距离为 x x x 的一次函数,询问路径 min 。边有边权

Sol

李超线段树+树剖

简单讲李超线段树:

支持区间加入一次函数,询问区间的一次函数最值。

修改复杂度为 O ( l o g 2 n ) O(log^2n) O(log2n) , 如果是全局加入就是 O ( l o g   n ) O(log\ n) O(log n)
查询复杂度为 O ( l o g   n ) O(log\ n) O(log n)

维护的信息:

flagp[u]   当前节点是否插入了一次函数
K[u],B[u]  表示当前节点区域中最优的一次函数
Min/Max[u] 区间的最小值或最大值,一般是在询问的是区间的时候才有必要维护 

李超线段树维护信息的方法就是线段树的标记永久化 , 似乎不能支持删除。

对于一个区间修改 , 如果当前区间没有加入过直线 , 那么直接加入。
如果已有直线,比较两条直线,如果一条完全优于另外一条 , 那么替换或者是直接返回。
如果各有优劣,则看哪一条在当前区间内优势的部分较多,保留多的那一个,劣的继续往下递归。
由于一次函数的单调性 , 如果我们保留长的在当前区间 , 那么往下递归插入的一次函数只用插向一边,因为只会存在一个优劣转换的 x x x ,这样长度每次减半,复杂度就是 O ( l o g   n ) O(log\ n) O(log n)
如果是区间插入 , 那么要先定位到 O ( l o g   n ) O(log\ n) O(log n) 个区间,复杂度就是 O ( l o g 2 n ) O(log^2n) O(log2n) 了。
对于比较那个点的优势区间更长,可以采取算交点的方式,但我一般采用比较中点函数值的大小来推算,稍加讨论就行了。

这一题直接这样做就行了 , 但是要明确 x x x 的意义 。它是从起点到当前的距离,这个不方便计算,直接转换成到根节点的距离就行了,因为是区间插入不影响整体答案。

code:

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
int n,m;
const int N=1e5+10;
#define ls (u<<1)
#define rs (u<<1|1)
typedef long long ll;
const ll INF=123456789123456789ll;
struct edge{int to,next,w;}a[N<<1];
int top[N],fa[N],id[N],dfn[N],dep[N],son[N],size[N],I=0;
ll dis[N];
struct node{
	ll k,b,flag,Min;
	node(){k=b=flag=0;Min=INF;}
	inline ll F(ll x){return k*dis[dfn[x]]+b;}
	inline void Fill(ll _k,ll _b,ll l,ll r) {
		k=_k,b=_b;flag=1;Min=min(Min,min(F(l),F(r)));
		return;
	}
}T[N<<2];int head[N],cnt=0;
inline ll F(ll k,ll b,ll x){return (ll)k*dis[dfn[x]]+b;}
inline void add(int x,int y,int w){a[++cnt]=(edge){y,head[x],w};head[x]=cnt;}
void dfs(int u){size[u]=1;
	for(int v,i=head[u];i;i=a[i].next){
		v=a[i].to;if(v==fa[u]) continue;
		fa[v]=u;dep[v]=dep[u]+1;dis[v]=dis[u]+a[i].w;dfs(v);
		size[u]+=size[v];if((!son[u])||size[v]>size[son[u]]) son[u]=v;
	}
	return;
}
void Dfs(int u,int tp){
	top[u]=tp;id[u]=++I,dfn[I]=u;
	if(!son[u]) return;Dfs(son[u],tp);
	for(int v,i=head[u];i;i=a[i].next) {v=a[i].to;if(v==fa[u]||v==son[u]) continue;Dfs(v,v);}
	return;
}
inline 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]];} return dep[u]<dep[v]? u:v;}
inline ll Dis(int u,int v){return dis[u]+dis[v]-(dis[LCA(u,v)]<<1);}
void Modify(int u,int l,int r,int L,int R,ll k,ll b){
	int mid=l+r>>1;
	if(l>=L&&r<=R){
		if(!T[u].flag) {T[u].Fill(k,b,l,r);return;}
		ll yln=F(k,b,l),yrn=F(k,b,r),ylo=T[u].F(l),yro=T[u].F(r);
		if(yln>=ylo&&yrn>=yro) return;
		if(yln<=ylo&&yrn<=yro) return T[u].Fill(k,b,l,r);
		else {
			ll ymn=F(k,b,mid),ymo=T[u].F(mid);
			if(ymn>=ymo) {
				if(yln>=ylo) Modify(rs,mid+1,r,L,R,k,b);
				else Modify(ls,l,mid,L,R,k,b);
			}
			else {
				if(yln>=ylo) Modify(ls,l,mid,L,R,T[u].k,T[u].b),T[u].Fill(k,b,l,r);
				else Modify(rs,mid+1,r,L,R,T[u].k,T[u].b),T[u].Fill(k,b,l,r);
			}
			T[u].Min=min(T[u].Min,min(T[ls].Min,T[rs].Min));
			return;
		}
		if(l==r) return;
	}
	if(mid>=L) Modify(ls,l,mid,L,R,k,b);
	if(mid< R) Modify(rs,mid+1,r,L,R,k,b);
	T[u].Min=min(T[u].Min,min(T[ls].Min,T[rs].Min));
	return;
}
inline void Insert(int s,int t,ll a,ll b){
	while(top[s]^top[t]) {Modify(1,1,n,id[top[s]],id[s],a,b);s=fa[top[s]];}
	Modify(1,1,n,id[t],id[s],a,b);
}
void Query(int u,int l,int r,int L,int R,ll&ans){
	if(l>=L&&r<=R) {ans=min(ans,T[u].Min);return;}
	int mid=l+r>>1;
	if(T[u].flag) ans=min(ans,min(T[u].F(max(l,L)),T[u].F(min(r,R))));
	if(l==r) return;
	if(mid>=R) Query(ls,l,mid,L,R,ans);
	else if(mid<L) Query(rs,mid+1,r,L,R,ans);
	else {
		Query(ls,l,mid,L,mid,ans);
		Query(rs,mid+1,r,mid+1,R,ans);
	}
	return;
}
inline ll Query(int s,int t){
	ll ans=INF;
	while(top[s]^top[t]){
		if(dep[top[s]]<dep[top[t]]) swap(s,t);
		Query(1,1,n,id[top[s]],id[s],ans);
		s=fa[top[s]];
	}
	if(dep[s]>dep[t]) swap(s,t);
	Query(1,1,n,id[s],id[t],ans);
	return ans;
}

int main()
{
	init(n),init(m);int u,v,w;
	for(int i=1;i<n;++i) init(u),init(v),init(w),add(u,v,w),add(v,u,w);
	dfs(1);Dfs(1,1);
	for(int i=1;i<=m;++i) {
		int op,s,t;
		init(op);init(s),init(t);
		if(op==1) {
			int lca=LCA(s,t);ll a,b;init(a),init(b);
			Insert(s,lca,-a,b+dis[s]*a);
			Insert(t,lca,a,b+(dis[s]-(dis[lca]<<1))*a);
		}
		else printf("%lld\n",Query(s,t));
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值