SDOI2016 游戏

传送门
仔细一看:每个节点动态维护半平面交??
一搜,原来是李超线段树。
把原树剖分一下,就转化为二维平面内如下操作:
①加上一条线段。
②询问一段区间内的最小值。

该线段树利用了标记永久化的思想。
每个区间 [ l , r ] [l,r] [l,r]只保留一条线段: k ∗ m i d + b k*mid+b kmid+b最小的那条。也就是与直线 x = m i d x=mid x=mid相交的最下面的一条。插入线段的时候根据交点维护这个东西即可:
①插入线段全部在当前节点最优线段上方,就一定没有贡献。
②插入线段全部在当前节点最优线段下方,就把当前节点最优线段换掉然后返回。因为不可能在子区间里使得之前的最优线段比当前插入线段更优,所以在询问的时候插入线段完全取代了之前的最优线段。
③不满足①,②就一定有交点。在继续下面的操作之前,先考虑当前最优线段是否可以被插入线段换掉(比较 m i d mid mid处函数值大小),可以的话就把两个线段交换,然后根据交点的位置递归下去插入到子区间里。这样复杂度是 O ( l o g n 2 ) O(log n^2) O(logn2)的。

其实就相当于是在这个半平面交中,每个外边缘的线段都找到了一个对应点。 这些边缘线段的贡献就由这些节点来存储。

由于要求区间最小,又由于一次函数的单调性,所以最小值一定在端点处取得。所以每个节点根据它中点处存储的“最优线段”可以得到一个最小值,每次插入后记得 p u s h u p pushup pushup一下就行了。

在询问区间最小的时候,当前节点的区间范围有可能会大于询问范围,所以每个节点的函数值要重新根据询问范围算一下。

由于最开始每个点都有一个极大值,所以每个点初始化都有一条斜率为0,截距为 123456789123456789 123456789123456789 123456789123456789的直线,就不用打标记了。

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::swap;
using std::min;
cs int N=1e5+10;
cs ll oo=123456789123456789ll;

namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-') f=-1;ch=gc();}
		while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
		return x*f;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;

int n,m,u,v,g,op;ll a,b,w;
inline void Min(ll &a,ll b){if(a>b)a=b;}
int Head[N],Next[N<<1],V[N<<1],cnt=0;ll W[N<<1],dis[N];
int dep[N],fa[N],son[N],siz[N],top[N],dfn[N],t[N],tot=0;

namespace SGT{
	#define lc (root<<1)
	#define rc (root<<1|1)
	#define mid (T[root].l+T[root].r>>1)
	struct Line{
		ll K,B;
		Line(ll k=0,ll b=oo){K=k,B=b;}
		inline ll val(ll x){return K*x+B;}
	};
	inline double cross(cs Line &a,cs Line &b){return (double)(a.B-b.B)/(double)(b.K-a.K);}
	struct node{
		int l,r;ll ans;Line L;
		node(){ans=oo;}
	}T[N<<2];
	inline void pushup(int root){
		if(T[root].l<T[root].r) Min(T[root].ans,T[lc].ans),Min(T[root].ans,T[rc].ans);
		Min(T[root].ans,T[root].L.val(dis[t[T[root].l]])),
		Min(T[root].ans,T[root].L.val(dis[t[T[root].r]]));
	}
	inline void build(int root,int l,int r){
		T[root].l=l,T[root].r=r;if(l==r) return;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	inline void update(int root,Line L){
		ll yl_1=T[root].L.val(dis[t[T[root].l]]),yr_1=T[root].L.val(dis[t[T[root].r]]);
		ll yl_2=L.val(dis[t[T[root].l]]),yr_2=L.val(dis[t[T[root].r]]);
		if(yl_1<=yl_2 && yr_1<=yr_2) return;
		if(yl_1> yl_2 && yr_1> yr_2){T[root].L=L;return pushup(root);}
		if(L.val(dis[t[mid]])<T[root].L.val(dis[t[mid]])) swap(L,T[root].L);
		double pos=cross(T[root].L,L);
		(pos<=dis[t[mid]])?update(lc,L):update(rc,L);
		return pushup(root);
	}
	inline void insert(int root,int l,int r,cs Line &L){
		if(l<=T[root].l&&T[root].r<=r) return update(root,L);
		if(l> mid) return insert(rc,l,r,L),pushup(root);
		if(r<=mid) return insert(lc,l,r,L),pushup(root);
		insert(lc,l,r,L),insert(rc,l,r,L),pushup(root);
	}
	inline ll query(int root,int l,int r,ll ret=oo){
		if(l<=T[root].l&&T[root].r<=r) return T[root].ans;
		Min(ret,T[root].L.val(dis[t[l]])),
		Min(ret,T[root].L.val(dis[t[r]]));
		if(r<=mid){Min(ret,query(lc,l,r));return ret;}
		if(l> mid){Min(ret,query(rc,l,r));return ret;}
		Min(ret,min(query(lc,l,mid),query(rc,mid+1,r)));return ret;
	}
}
using namespace SGT;

inline void add(int u,int v,ll w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
void dfs1(int u,int f){
	siz[u]=1,son[u]=0,dep[u]=dep[f]+1,fa[u]=f;
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])if(v^f){
		dis[v]=dis[u]+W[i],dfs1(v,u),siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
}
void dfs2(int u,int tp){
	t[dfn[u]=++tot]=u,top[u]=tp;
	if(son[u]) dfs2(son[u],tp);
	for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]])
		if((v^fa[u])&&(v^son[u])) dfs2(v,v);
}
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 void modify(int u,int v,int g,ll a,ll b,bool flag=0){
	int U=u,V=v;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v),flag^=1;
		if(!flag) insert(1,dfn[top[u]],dfn[u],Line(-a,a*dis[U]+b));
		else insert(1,dfn[top[u]],dfn[u],Line(a,a*(dis[U]-dis[g]*2)+b));
		u=fa[top[u]];
	}if(dep[u]<dep[v]) swap(u,v),flag^=1;
	if(!flag) insert(1,dfn[v],dfn[u],Line(-a,a*dis[U]+b));
	else insert(1,dfn[v],dfn[u],Line(a,a*(dis[U]-dis[g]*2)+b));
}
inline ll ask(int u,int v,ll ret=oo){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		Min(ret,query(1,dfn[top[u]],dfn[u]));
		u=fa[top[u]];
	}if(dep[u]<dep[v]) swap(u,v);
	Min(ret,query(1,dfn[v],dfn[u]));
	return ret;
}

int main(){
//	freopen("2561.in","r",stdin);
	n=gi(),m=gi();
	for(int re i=1;i< n;++i)
		u=gi(),v=gi(),w=gl(),add(u,v,w),add(v,u,w);
	dfs1(1,0),dfs2(1,1),build(1,1,n);
	while(m--){
		op=gi();
		if(op==1) u=gi(),v=gi(),g=lca(u,v),a=gl(),b=gl(),modify(u,v,g,a,b);
		if(op==2) u=gi(),v=gi(),printf("%lld\n",ask(u,v));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值