1. 程式人生 > >【BZOJ4515】[Sdoi2016]遊戲 樹鏈剖分+線段樹

【BZOJ4515】[Sdoi2016]遊戲 樹鏈剖分+線段樹

以及 hint odi 區間 char alice return clas cstring

【BZOJ4515】[Sdoi2016]遊戲

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

題解:很神的線段樹~

一個操作相當於加入一條線段,一個詢問相當於求一段區間的最小值。那麽我們線段樹的每個節點都要維護這些東西:左右端點的dis值,區間中最小的數,以及這段區間中最優的線段。但是如果又新來了一條線段呢?需要分類討論了:

如果舊線段在左右端點的取值都比新線段優,則新線段顯然沒用;如果新線段在左右斷電的取值都比舊線段有,則舊線段顯然沒用;否則呢?遞歸下去更新即可。

就這麽完事了?嗯。時間復雜度?那就再來一個log唄,$O(nlog^3_n)$。(再來一個log你會更強)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=100010;
typedef long long ll;
const ll inf=123456789123456789ll;
int n,m,cnt;
int to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],fa[maxn];
int p[maxn],q[maxn];
ll sa[maxn<<2],sb[maxn<<2],sl[maxn<<2],sr[maxn<<2],sn[maxn<<2],dis[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void dfs1(int x)
{
	siz[x]=1;
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
	{
		fa[to[i]]=x,dep[to[i]]=dep[x]+1,dis[to[i]]=dis[x]+val[i],dfs1(to[i]),siz[x]+=siz[to[i]];
		if(siz[to[i]]>siz[son[x]])	son[x]=to[i];
	}
}
void dfs2(int x,int tp)
{
	top[x]=tp,p[x]=++q[0],q[q[0]]=x;
	if(son[x])	dfs2(son[x],tp);
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x]&&to[i]!=son[x])	dfs2(to[i],to[i]);
}
void build(int l,int r,int x)
{
	sn[x]=sb[x]=inf;
	if(l==r)
	{
		sl[x]=sr[x]=dis[q[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson),build(mid+1,r,rson);
	sl[x]=sl[lson],sr[x]=sr[rson];
}
void updata(int l,int r,int x,int a,int b,ll c,ll d)
{
	int mid=(l+r)>>1;
	if(a<=l&&r<=b)
	{
		if(sl[x]*sa[x]+sb[x]>sl[x]*c+d&&sr[x]*sa[x]+sb[x]>sr[x]*c+d)	sa[x]=c,sb[x]=d;
		else	if(sl[x]*sa[x]+sb[x]>sl[x]*c+d||sr[x]*sa[x]+sb[x]>sr[x]*c+d)
			updata(l,mid,lson,a,b,c,d),updata(mid+1,r,rson,a,b,c,d);
		sn[x]=min(sn[x],min(sl[x]*c+d,sr[x]*c+d));
		if(l!=r)	sn[x]=min(sn[x],min(sn[lson],sn[rson]));
		return ;
	}
	if(a<=mid)	updata(l,mid,lson,a,b,c,d);
	if(b>mid)	updata(mid+1,r,rson,a,b,c,d);
	sn[x]=min(sn[x],min(sn[lson],sn[rson]));
	return ;
}
ll query(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)	return sn[x];
	ll ret=min(dis[q[a]]*sa[x]+sb[x],dis[q[b]]*sa[x]+sb[x]);
	int mid=(l+r)>>1;
	if(a<=mid)	ret=min(ret,query(l,mid,lson,a,min(b,mid)));
	if(b>mid)	ret=min(ret,query(mid+1,r,rson,max(a,mid+1),b));
	return ret;
}
inline int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])	x=fa[top[x]];
		else	y=fa[top[y]];
	}
	if(dep[x]>dep[y])	return y;
	return x;
}
inline void modify()
{
	int x=rd(),y=rd();
	ll a=rd(),b=rd(),c=dis[x]*a+b,d=(dis[x]-2*dis[lca(x,y)])*a+b;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]])	updata(1,n,1,p[top[x]],p[x],-a,c),x=fa[top[x]];
		else	updata(1,n,1,p[top[y]],p[y],a,d),y=fa[top[y]];
	}
	if(dep[x]<dep[y])	updata(1,n,1,p[x],p[y],a,d);
	else	updata(1,n,1,p[y],p[x],-a,c);
}
inline void ask()
{
	int x=rd(),y=rd();
	ll ret=inf;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])	swap(x,y);
		ret=min(ret,query(1,n,1,p[top[x]],p[x])),x=fa[top[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	ret=min(ret,query(1,n,1,p[x],p[y]));
	printf("%lld\n",ret);
}
int main()
{
	n=rd(),m=rd();
	int i,a,b,c;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
	dep[1]=1,dfs1(1),dfs2(1,1);
	build(1,n,1);
	for(i=1;i<=m;i++)
	{
		if(rd()==1)	modify();
		else	ask();
	}
	return 0;
}//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

【BZOJ4515】[Sdoi2016]遊戲 樹鏈剖分+線段樹