Loading

SDOI2016 题解

Lnk

首先树剖,然后变成在 \(\text{dfn}\) 区间上插一个关于 \(\text{dis}\) 的一次函数。这个很神奇,一般的李超树是,在 \(x\) 轴区间上插入关于 \(x\) 的一次函数。然而这里,\(\text{dfn}\)\(\text{dis}\) 看起来毫无关系。

考虑李超树运用了线段的什么性质。无外乎,单调性线性性

第一个显然,你树剖出来的 \(\text{dfn}\) 连续段是一条链,\(\text{dis}\)\(\text{dfn}\) 单调递增,那关于 \(\text{dis}\) 单调的函数自然也关于 \(\text{dfn}\) 单调。所以李超树维护的实际上是一堆折线。

有人就说,你只是在某些区间满足这个单调性,整棵树维护的东西不是乱套?管你吗的整棵树,修改查询的所有区间都合法就能满足我的要求,靠近根的那些点根本查不到。

对于线性性,它实际上是在满足这个:

if(f(ln)<g(now)(ln)) dnf(ls(now),ln,mid,f);
if(f(rn)<g(now)(rn)) dnf(rs(now),mid+1,rn,f);

就是说,如果线段 \(f\) 的两端点分别高于 \(g\),那么 \(f\) 就没有任何一处的取值比 \(g\) 小。乍一看折线好像不行:

但是这里的折线不一般。注意到 \(\text{dfn}\)\(\text{dis}\) 是一个映射,图中线段斜率 \(\text{dis}\times a\)(设关于 \(\text{dis}\) 函数的斜率是 \(a\)),那么如果折线 \(f\) 在某一处的斜率比 \(g\) 要大,\(f\) 所对应原函数的 \(a\) 也要更大,\(f\) 的斜率便永远大于 \(g\),上图相交两次的情况根本不存在。

然后注意一次函数求值要把对应的 \(\text{dis}\) 而不是 \(\text{dfn}\) 代进去就是了。

#include <cstdio>
#include <vector>
#include <algorithm>
#define ll long long
#define int long long

using namespace std;

const int N=114514;
const ll inf=123456789123456789ll;

ll dis[N];

struct lin{
	int k;ll b;ll operator()(int x){return k*dis[x]+b;}
};

namespace sgt{
	#define ls(x) (x<<1)
	#define rs(x) (x<<1|1)
	struct node{
		lin g;ll mn;
		#define g(x) t[x].g
		#define mn(x) t[x].mn
	}t[N<<2];
	void updata(int x,int l,int r){
		if(l!=r) mn(x)=min(mn(ls(x)),mn(rs(x)));
		mn(x)=min(mn(x),min(g(x)(l),g(x)(r)));
	}
	void build(int now,int ln,int rn){
		g(now).b=mn(now)=inf;
		if(ln==rn) return ;
		int mid=ln+rn>>1;
		build(ls(now),ln,mid);
		build(rs(now),mid+1,rn);
	}
	void dnf(int now,int ln,int rn,lin f){
		if(g(now).b==inf) return g(now)=f,updata(now,ln,rn);
		int mid=ln+rn>>1;
		if(f(mid)<g(now)(mid)) swap(f,g(now));
		if(ln==rn) return updata(now,ln,rn);
		if(f(ln)<g(now)(ln)) dnf(ls(now),ln,mid,f);
		if(f(rn)<g(now)(rn)) dnf(rs(now),mid+1,rn,f);
		updata(now,ln,rn);
	}
	void upd(int now,int ln,int rn,int l,int r,lin f){
		//printf("upd [%d %d] %d %d %dx+%d\n",ln,rn,l,r,f.k,f.b);
		if(l<=ln&&rn<=r) return dnf(now,ln,rn,f);
		int mid=ln+rn>>1;
		if(l<=mid) upd(ls(now),ln,mid,l,r,f);
		if(r>mid) upd(rs(now),mid+1,rn,l,r,f);
		updata(now,ln,rn);
	}
	ll qry(int now,int ln,int rn,int l,int r){
		//printf("qry [%d %d] %d %d\n",ln,rn,l,r);
		if(l<=ln&&rn<=r) return /*printf("re res %lld\n",mn(now)),*/mn(now);
		int mid=ln+rn>>1;
		ll res=min(g(now)(max(ln,l)),g(now)(min(rn,r)));
		//printf("res %lld\n",res);
		if(l<=mid) res=min(res,qry(ls(now),ln,mid,l,r));
		if(r>mid) res=min(res,qry(rs(now),mid+1,rn,l,r));
		return res;
	}
}

struct edg{int v,w;};

vector<edg> vc[N];

int fa[N],dep[N],siz[N],son[N];ll td[N];

void dfs1(int now,int lst){
	fa[now]=lst;dep[now]=dep[lst]+1;siz[now]=1;
	for(auto[v,w]:vc[now]) if(v!=lst){
		td[v]=td[now]+w;dfs1(v,now);siz[now]+=siz[v];
		if(siz[v]>siz[son[now]]) son[now]=v;
	}
}

int top[N],dfn[N],dcnt;

void dfs2(int now,int tp){
	top[now]=tp;dis[dfn[now]=++dcnt]=td[now];
	if(son[now]) dfs2(son[now],tp);
	for(auto[v,w]:vc[now]) if(!dfn[v]) dfs2(v,v);
}

struct itv{int l,r;}up[N],dw[N];int uc,dc,n;

void upd(int x,int y,int a,int b){
	uc=dc=0;int s=dfn[x],c;
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]]) up[++uc]=(itv){dfn[top[x]],dfn[x]},x=fa[top[x]];
		else dw[++dc]=(itv){dfn[top[y]],dfn[y]},y=fa[top[y]];
	}
	if(dep[x]>dep[y]) up[++uc]=(itv){dfn[y],dfn[x]},c=dfn[y];
	else dw[++dc]=(itv){dfn[x],dfn[y]},c=dfn[x];
	for(int i=1;i<=uc;++i) sgt::upd(1,1,n,up[i].l,up[i].r,(lin){-a,a*dis[s]+b});
	for(int i=dc;i;--i) sgt::upd(1,1,n,dw[i].l,dw[i].r,(lin){a,a*(dis[s]-2*dis[c])+b});
}

ll qry(int x,int y){
	ll res=inf;while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res=min(res,sgt::qry(1,1,n,dfn[top[x]],dfn[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	return min(res,sgt::qry(1,1,n,dfn[x],dfn[y]));
}

signed main()
{
	freopen("data.txt","r",stdin);
	freopen("myass.txt","w",stdout);
	int m,x,y,v,w;scanf("%lld%lld",&n,&m);
	for(int i=1;i<n;++i) scanf("%lld%lld%lld",&x,&y,&w),
		vc[x].push_back((edg){y,w}),vc[y].push_back((edg){x,w});
	dfs1(1,0);dfs2(1,1);sgt::build(1,1,n);
	//puts("Dfn");for(int i=1;i<=n;++i) printf("%d ",dfn[i]);puts("");
	while(m--){int f;scanf("%lld%lld%lld",&f,&x,&y);switch(f){
		case 1:scanf("%lld%lld",&v,&w);upd(x,y,v,w);break;
		case 2:printf("%lld\n",qry(x,y));break;
	}}
}
posted @ 2023-08-09 16:38  Albertvαn  阅读(7)  评论(0编辑  收藏  举报