【树链剖分&李超线段树】【 [SDOI2016]游戏】【题解】


【树链剖分&李超线段树】【 [SDOI2016]游戏】【题解】

前置知识:

[SDOI2016]游戏

题目大意

给定一棵树,每次在一条链上加入一条以深度为下标的线段,每次查询一条链上的最小值。

Solution

加入线段和查询最值,可以想到用线段树维护,又因为是对树上的链进行操作,而查询的信息不满足差分性质,所以考虑树链剖分+李超线段树。
但是本题中,线段在点x的值不再是\(k*x+b\),而是\(k*dis[x]+b\),也就是说,我们插入的可能不再是条线段,而是一条可能有多个拐点的折线,这仍然能用李超树维护吗?
事实上,只要保证插入的两个函数最多只有一个交点(或是重合),就能用李超树维护。插入的折线相当于是将横坐标离散化后的线段,所以任意两条线段最多只有一个交点(如果不重合),满足李超树的适用范围。
而且本题需要区间查询最小值,只需对李超树的每个节点保存一个最小值,在当前节点区间的范围在查询范围之内时,直接返回最小值,不在往下递归。但需要注意的是,由于李超树基于标记永久化实现,在查询过程中遇到的点依然需要累积到答案中去。
复杂度:O(\(Nlog^3N\))

Code

#include
using namespace std;
#define int long long
inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) {putchar('-');x=~(x-1);}
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=1e5+100,inf=123456789123456789;
int n,m,d[N],top[N],siz[N],hs[N],dfn[N],fa[N],id[N];
struct node{
	int l,r,k,b,minn;
}t[N<<2];
struct E{
	int nxt,to,w;
}e[N<<1];
int cnt,hd[N],tot;
void adde(int x,int y,int z)
{
	e[++cnt].nxt=hd[x];
	e[cnt].w=z;
	e[cnt].to=y;
	hd[x]=cnt;
}
void dfs1(int x)
{
	siz[x]=1;
	for(int i=hd[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa[x]) continue;
		fa[y]=x;
		d[y]=d[x]+e[i].w;
		dfs1(y);
		siz[x]+=siz[y];
		if(siz[y]>siz[hs[x]]) hs[x]=y;
	}
}
void dfs2(int x)
{
	dfn[x]=++tot;
	id[tot]=x;
	if(hs[x]){
		top[hs[x]]=top[x];
		dfs2(hs[x]);
	}
	for(int i=hd[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(dfn[y]) continue;
		top[y]=y;
		dfs2(y);
	}
}
void build(int x,int l,int r)
{
	t[x].l=l,t[x].r=r;
	t[x].k=0;t[x].b=inf;
	t[x].minn=inf;
	if(l==r) return;
	int mid=l+r>>1;
	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
void pushup(int x)
{
	t[x].minn=min(t[x].minn,min(t[x<<1].minn,t[x<<1|1].minn));
}
int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(d[top[x]]=l&&t[x].r<=r)
	{
		return t[x].minn;
	}
	int res=min(t[x].k*max(d[id[l]],d[id[t[x].l]])+t[x].b,t[x].k*min(d[id[r]],d[id[t[x].r]])+t[x].b);
	int mid=t[x].l+t[x].r>>1;
	if(l<=mid) res=min(res,query(x<<1,l,r));
	if(r>mid) res=min(res,query(x<<1|1,l,r));
	return res;
}
int ask(int x,int y)
{
	int g=lca(x,y);
	int res=inf;
	while(top[x]!=top[g]) res=min(res,query(1,dfn[top[x]],dfn[x])),x=fa[top[x]];
	while(top[y]!=top[g]) res=min(res,query(1,dfn[top[y]],dfn[y])),y=fa[top[y]];
	if(dfn[x]>1;
	if(t[x].l>=l&&t[x].r<=r)
	{
		int y[2][2];
		y[0][0]=t[x].k*d[id[t[x].l]]+t[x].b;
		y[0][1]=t[x].k*d[id[t[x].r]]+t[x].b;
		y[1][0]=k*d[id[t[x].l]]+b;
		y[1][1]=k*d[id[t[x].r]]+b;
		if(y[1][0]>=y[0][0]&&y[1][1]>=y[0][1])return;
		if(y[1][0]<=y[0][0]&&y[1][1]<=y[0][1]){
			update(x,k,b,min(y[1][0],y[1][1]));
			return;
		}
		int valmid[2]={t[x].k*d[id[mid]]+t[x].b,k*d[id[mid]]+b};
		if(valmid[0]<=valmid[1])
		{
			if(y[0][0]<=y[1][0]) change(x<<1|1,l,r,k,b);
			else change(x<<1,l,r,k,b);
		}
		else
		{
			if(y[0][0]<=y[1][0]) change(x<<1,l,r,t[x].k,t[x].b),update(x,k,b,min(y[1][1],y[1][0]));
			else change(x<<1|1,l,r,t[x].k,t[x].b),update(x,k,b,min(y[1][1],y[1][0]));
		}
		pushup(x);
		return;
	}
	if(l<=mid) change(x<<1,l,r,k,b);
	if(r>mid) change(x<<1|1,l,r,k,b);
	pushup(x);
}
void add(int x,int y,int k,int b)
{
	int g=lca(x,y),dis=d[x];
	while(top[x]!=top[g])
	{
		change(1,dfn[top[x]],dfn[x],-k,b+k*dis);
		x=fa[top[x]];
	}
	while(top[y]!=top[g])
	{
		change(1,dfn[top[y]],dfn[y],k,b+k*(dis-2*d[g]));
		y=fa[top[y]];
	}
	if(dfn[x]