bzoj4515【SDOI2016】游戏

4515: [Sdoi2016]游戏

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 290   Solved: 124
[ Submit][ Status][ Discuss]

Description

Alice 和 Bob 在玩一个游戏。
游戏在一棵有 n 个点的树上进行。最初,每个点上都只有一个数字,那个数字是 123456789123456789。
有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。对于路径上的一个点 r,
若 r 与 s 的距离是 dis,那么 Alice 在点 r 上添加的数字是 a×dis+b。有时,Bob 会选择一条从 s 到 t 的路径。
他需要先从这条路径上选择一个点,再从那个点上选择一个数字。
Bob 选择的数字越小越好,但大量的数字让 Bob 眼花缭乱。Bob 需要你帮他找出他能够选择的最小的数字。

Input

第一行两个数字 n、m,表示树的点数和进行的操作数。
接下来 n−1 行,每行三个数字 u、v、w,表示树上有一条连接 u、v 的边,长度是 w。
接下来 m 行。每行第一个数字是 1 或 2。
若第一个数是 1,表示 Alice 进行操作,接下来四个数字 s、t、a、b。
若第一个数是 2,表示 Bob 进行操作,接下来四个数字 s、t。

Output

每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字

Sample Input

3 5
1 2 10
2 3 20
2 1 3
1 2 3 5 6
2 2 3
1 2 3 -5 -6
2 2 3

Sample Output

123456789123456789
6
-106

HINT

 n≤100000,m≤100000,∣a∣≤10000,0<=w,|b|<=10^9

Source

鸣谢Menci上传





树链剖分+线段树

对于树上链的操作,通过树链剖分,转化为区间操作。

然后形如a*dis+b的式子,可以转化成一条线段,于是问题转化成动态维护区间内线段的最低点。

这是一个比较经典的问题,用线段树的标记永久化就可以实现。(具体方法见代码)




#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100005
#define inf 123456789123456789ll
using namespace std;
int n,m,cnt,tot;
int head[maxn],sz[maxn],fa[maxn],son[maxn],pos[maxn],id[maxn],top[maxn];
ll ans,d[maxn];
struct edge_type{int next,to;ll v;}e[maxn*2];
struct seg{ll a,b,mn;}t[maxn*4];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y,int z)
{
	e[++cnt]=(edge_type){head[x],y,z};head[x]=cnt;
	e[++cnt]=(edge_type){head[y],x,z};head[y]=cnt;
}
inline void dfs1(int x)
{
	sz[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if (y!=fa[x])
		{
			d[y]=d[x]+e[i].v;fa[y]=x;
			dfs1(y);
			sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
inline void dfs2(int x,int chain)
{
	top[x]=chain;pos[x]=++tot;id[tot]=x;
	if (son[x]) dfs2(son[x],chain);
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if (y!=son[x]&&y!=fa[x]) dfs2(y,y);
	}
}
inline void build(int k,int l,int r)
{
	t[k].a=0;t[k].b=inf;t[k].mn=inf;
	if (l==r) return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
inline ll calc(ll a,ll b,ll x)
{
	return a*d[id[x]]+b;
}
inline void update(int k,int l,int r,ll a,ll b)
{
	ll xl=calc(t[k].a,t[k].b,l),xr=calc(t[k].a,t[k].b,r),yl=calc(a,b,l),yr=calc(a,b,r);
	if (xl<=yl&&xr<=yr) return;
	if (xl>=yl&&xr>=yr){t[k].a=a;t[k].b=b;return;}
	int mid=(l+r)>>1;
	ll xm=calc(t[k].a,t[k].b,mid),ym=calc(a,b,mid);
	if (xm>=ym){swap(t[k].a,a);swap(t[k].b,b);swap(xl,yl);swap(xr,yr);swap(xm,ym);}
	if (xl>=yl) update(k<<1,l,mid,a,b);
	else update(k<<1|1,mid+1,r,a,b);
}
inline void add(int k,int l,int r,int L,int R,ll a,ll b)
{
	t[k].mn=min(t[k].mn,min(calc(a,b,L),calc(a,b,R)));
	if (l==L&&r==R){update(k,l,r,a,b);return;}
	int mid=(l+r)>>1;
	if (R<=mid) add(k<<1,l,mid,L,R,a,b);
	else if (L>mid) add(k<<1|1,mid+1,r,L,R,a,b);
	else add(k<<1,l,mid,L,mid,a,b),add(k<<1|1,mid+1,r,mid+1,R,a,b);
}
inline void query(int k,int l,int r,int L,int R)
{
	ans=min(ans,min(calc(t[k].a,t[k].b,L),calc(t[k].a,t[k].b,R)));
	if (l==L&&r==R){ans=min(ans,t[k].mn);return;}
	int mid=(l+r)>>1;
	if (R<=mid) query(k<<1,l,mid,L,R);
	else if (L>mid) query(k<<1|1,mid+1,r,L,R);
	else query(k<<1,l,mid,L,mid),query(k<<1|1,mid+1,r,mid+1,R);
}
inline void solveadd(int f,int x,ll a,ll b)
{
	while (top[x]!=top[f])
	{
		add(1,1,n,pos[top[x]],pos[x],a,b);
		x=fa[top[x]];
	}
	add(1,1,n,pos[f],pos[x],a,b);
}
inline void solvequery(int f,int x)
{
	while (top[x]!=top[f])
	{
		query(1,1,n,pos[top[x]],pos[x]);
		x=fa[top[x]];
	}
	query(1,1,n,pos[f],pos[x]);
}
inline int lca(int x,int y)
{
	for(;top[x]!=top[y];x=fa[top[x]]) if (d[top[x]]<d[top[y]]) swap(x,y);
	return d[x]<d[y]?x:y;
}
int main()
{
	n=read();m=read();
	F(i,1,n-1)
	{
		int x=read(),y=read(),z=read();
		add_edge(x,y,z);
	}
	dfs1(1);dfs2(1,1);
	build(1,1,n);
	while (m--)
	{
		int opt=read(),x=read(),y=read();
		if (opt==1)
		{
			ll a=read(),b=read();
			int lc=lca(x,y);
			solveadd(lc,x,-a,a*d[x]+b);
			solveadd(lc,y,a,a*(d[x]-d[lc]*2)+b);
		}
		else
		{
			int lc=lca(x,y);
			ans=inf;
			solvequery(lc,x);solvequery(lc,y);
			printf("%lld\n",ans);
		}
	}
}


你可能感兴趣的:(线段树,树链剖分,bzoj,标记永久化)