1. 程式人生 > 實用技巧 >P4069 [SDOI2016]遊戲 樹剖+李超樹

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;
}