P4069 [SDOI2016]游戏 树剖+李超树

题意:

戳这里

分析:

首先树上路径操作,要用树剖

其次,操作等价于区间加一条线段,然后查询区间最小值

但是与普通李超树不同,这次 \(x\) 值并不连续,每次插入的线段下标是和给定起点的距离,那么我们更改一下维护的信息,线段树上 \(a\) 点的 $x $ 值是它离根的距离,然后我们将操作转化,每次插入的线段分成两条

  1. \(s\to lca\)

这一部分距离随编号减小而增大,所以斜率由 \(k\) 变为 \(-k\),\(b\) 的初值就是 \(dis[s]*k+b\)

  1. \(lca\to t\)

这一部分距离随编号增大而增大,,那么斜率不变, \(b\) 值变为 \((dis[s]-(dis[lca]*2)*k+b)\)

之后就是维护区间最小值,但注意由于标记永久化的操作,我们需要每一次将答案和对应区间内 \(max(l,ql)\) ~ \(min(r,qr)\) 的部分的最小值取 \(min\)

代码:

#include<bits/stdc++.h>
#define lc (rt<<1)
#define rc (rt<<1)|1
using namespace std;

namespace zzc
{
	long long read()
	{
		long long x=0,f=1;char ch=getchar();
		while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
		while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 1e5+5;
	long long n,m,cnt,idx;
	long long head[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn],dep[maxn],dfn[maxn],dis[maxn],id[maxn];
	
	struct edge
	{
		int to,nxt,val;
	}e[maxn<<1];
	
	void add(int u,int v,int w)
	{
		e[++cnt].to=v;
		e[cnt].nxt=head[u];
		e[cnt].val=w;
		head[u]=cnt;
	}
	
	struct line
	{
		long long k,b;
	    line (long long k=0,long long b=0):k(k),b(b){}
		long long calc(long long pos)
		{
			return k*pos+b;
		}
	};
	
	struct node
	{
		line L;
		long long val;
		bool flag;
		node(line L,long long val,bool flag=false):L(L),val(val),flag(flag){}
		node(){}
	}t[maxn<<2];
	
	void dfs1(int u,int ff)
	{
		dep[u]=dep[ff]+1;fa[u]=ff;siz[u]=1;son[u]=0;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==ff) continue;
			dis[v]=dis[u]+e[i].val;
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]]) son[u]=v;
		}
	}
	
	void dfs2(int u,int bel)
	{
		top[u]=bel;dfn[u]=++idx;id[idx]=u;
		if(son[u]) dfs2(son[u],bel);
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==fa[u]||v==son[u]) continue;
			dfs2(v,v);
		}
	}
	
	void pushup(int rt)
	{
		t[rt].val=min(t[rt].val,min(t[lc].val,t[rc].val));
	}
	
	void build(int rt,int l,int r)
	{
		t[rt]=node(line(0,123456789123456789ll),123456789123456789ll,true);
		if(l==r) return ;
		int mid=(l+r)>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		pushup(rt);
	}
	
	int lca(int x,int y)
	{
		while(top[x]!=top[y])
		{
			if(dep[top[x]]<dep[top[y]]) swap(x,y);
			x=fa[top[x]];
		}
		return dep[x]>dep[y]?y:x;
	}
	
	void update(int rt,int l,int r,int ql,int qr,line x)
	{
		if(ql<=l&&r<=qr)
		{
			long long ll=dis[id[l]];
			long long rr=dis[id[r]];
			long long mid=(l+r)>>1,mmid=dis[id[mid]];
			long long lp=x.calc(ll),rp=x.calc(rr);
			long long lq=t[rt].L.calc(ll),rq=t[rt].L.calc(rr);
			if(!t[rt].flag) t[rt].flag=true,t[rt].L=x;
			else if(lp<lq&&rp<rq) t[rt].L=x;
			else if(lp<lq||rp<rq)
			{
				if(x.calc(mmid)<t[rt].L.calc(mmid)) swap(x,t[rt].L);
				if(x.calc(ll)<t[rt].L.calc(ll)) update(lc,l,mid,ql,qr,x);
				else update(rc,mid+1,r,ql,qr,x);
			}
			t[rt].val=min(t[rt].val,min(t[rt].L.calc(ll),t[rt].L.calc(rr)));
			if(l!=r) pushup(rt);
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid) update(lc,l,mid,ql,qr,x);
		if(qr>mid) update(rc,mid+1,r,ql,qr,x);
		if(l!=r) pushup(rt);
	}
	
	long long query(int rt,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr) return t[rt].val;
		int mid=(l+r)>>1;
		long long res=123456789123456789ll;
		if(t[rt].flag) res=min(res,min(t[rt].L.calc(dis[id[max(l,ql)]]),t[rt].L.calc(dis[id[min(r,qr)]])));
		if(ql<=mid) res=min(res,query(lc,l,mid,ql,qr));
		if(qr>mid) res=min(res,query(rc,mid+1,r,ql,qr));
		return res;
	}
	
	void update_tree(int u,int v,long long k,long long b)
	{
		line x=line(k,b);
		while(top[u]!=top[v])
		{
			update(1,1,n,dfn[top[u]],dfn[u],x);
			u=fa[top[u]];
		}
		update(1,1,n,dfn[v],dfn[u],x);
	}
	
	long long query_tree(int s,int t)
	{
		long long res=123456789123456789;
		while(top[s]!=top[t])
		{
			if(dep[top[s]]<dep[top[t]]) swap(s,t);
			res=min(res,query(1,1,n,dfn[top[s]],dfn[s]));
			s=fa[top[s]];
		}
		if(dep[s]>dep[t]) swap(s,t);
		res=min(res,query(1,1,n,dfn[s],dfn[t]));
		return res;
	}
	
	void work()
	{
		long long opt,s,t,a,b,c;
		n=read();m=read();
		for(int i=1;i<n;i++)
		{
			a=read();b=read();c=read();
			add(a,b,c);add(b,a,c);
		}
		dfs1(1,0);dfs2(1,1);
		build(1,1,n);
		for(int i=1;i<=m;i++)
		{
			opt=read();s=read();t=read();
			if(opt==1)
			{
				a=read();b=read();
				int tmp=lca(s,t);
				update_tree(s,tmp,-a,dis[s]*a+b);
				update_tree(t,tmp,a,(dis[s]-(dis[tmp]<<1))*a+b);
			}
			else
			{
				printf("%lld\n",query_tree(s,t));
			}
		}
	}
	
}

int main()
{
	zzc::work();
	return 0;
} 
上一篇:题解 LuoguP4071 【[SDOI2016]排列计数】


下一篇:P4068 [SDOI2016]数字配对