POJ3728,The merchant(倍增LCA+分治)

news/2024/7/4 1:46:05 标签: LCA, 思维, 分治

题意:有n个城市,有n-1条边使各个城市相互直接或间接连通,给出一件货物在各城市的价格w[n],然后给出q个询问,每个询问有两个城市s,t,问从s到t的路径上买入卖出货物盈利的最大值。注意:在某个城市买入,只能够在其后经过的城市卖出。

分析:假设询问从城市u到城市v的路径上所能获得的最大盈利值,又设lca为u,v的LCA,则u->v的路径就是u->lca->v,那么最大的盈利值就有可能出现在3种情况:
1.在u->lca中出现;
2.在lca->v中出现;
3.在u->lca->v中出现(即有跨过lca)。

第3种情况是比较容易解决的,我们只要求出lca->v中的最大值max_val,减去u->lca中的最小值min_val,max_val-min_val就是答案,因此对于这种情况我们可以设置ma[i][j],mi[i][j]分别表示:
ma[i][j]:在i结点往上跳2j步到达的结点的路径当中的最大价格;
mi[i][j]:在i结点往上跳2j步到达的结点的路径当中的最小价格。

对于第1,2种情况,我们同样可以考虑分治。以第1种情况为例,设k为u->lca路径当中某个结点,那么u->lca可分解为u->k,k->lca。那么最终答案就同样会有3种情况:
1 .在u->k中出现;
2 .在k->lca中出现;
3 .在u->lca中出现(即跨过结点k)。

仿照第3种情况,我们同样可以用倍增的思想来解决。设置up[i][j]表示:
up[i][j]:在i结点往上跳2j步到达的结点的路径当中,最大的价格差(当然,最大价格出现的位置一定要在最小价格出现的位置之后)。
根据上述分析,不难推出up[i][j]的更新方式:
up[i][j]=max(max(up[i][j-1],up[k][j-1]), ma[k][j]-mi[i][j])

其中k=fa[i][j-1],即在i结点往上跳2j-1步到达的结点。
第2种情况与第3种情况相似,可以设置down[i][j]表示:
down[i][j]:设k为在i结点往上跳2j步到达的结点,down[i][j]为k->i的路径当中,最大的价格差。

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int depth[maxn],fa[maxn][20];
ll ma[maxn][20],mi[maxn][20];
ll up[maxn][20],down[maxn][20];
ll w[maxn];
bool vis[maxn];
vector<int> G[maxn];

inline void read(ll &ret)
{
	char c;
	while((c=getchar())&&(c>'9'||c<'0'));
	ret=0;
	while(c>='0'&&c<='9')	ret=ret*10+c-'0', c=getchar();
}

inline void out(ll x)
{
	if(x>9)	out(x/10);
	putchar(x%10+'0');
}

void DFS(int u,int pre,int d)
{
	fa[u][0]=pre;	ma[u][0]=max(w[u],w[pre]);	
	up[u][0]=max(0ll,w[pre]-w[u]);	
	vis[u]=1;	depth[u]=d;
	if(u!=1)	mi[u][0]=min(w[u],w[pre]), down[u][0]=max(0ll,w[u]-w[pre]);
	else mi[u][0]=w[u],	down[u][0]=0;
	for(int i=0;i<G[u].size();++i)
	{
		if(!vis[G[u][i]])
		{
			DFS(G[u][i],u,d+1);
		}
	}
}

void init(int n)
{
	for(int i=1;i<=n;++i)	vis[i]=0;
	w[0]=0;
	DFS(1,0,1);
	for(int j=1;(1<<j)<n;++j)
		for(int i=1;i<=n;++i)
		{
			if(!fa[i][j-1])
			{
				fa[i][j]=fa[i][j-1];	ma[i][j]=ma[i][j-1];	
				mi[i][j]=mi[i][j-1];	up[i][j]=up[i][j-1];
				down[i][j]=down[i][j-1];
			}
			else
			{
				fa[i][j]=fa[fa[i][j-1]][j-1];
				ma[i][j]=max(ma[i][j-1],ma[fa[i][j-1]][j-1]);
				mi[i][j]=min(mi[i][j-1],mi[fa[i][j-1]][j-1]);
				up[i][j]=max(max(up[i][j-1],up[fa[i][j-1]][j-1]),ma[fa[i][j-1]][j-1]-mi[i][j-1]);
				down[i][j]=max(max(down[i][j-1],down[fa[i][j-1]][j-1]),ma[i][j-1]-mi[fa[i][j-1]][j-1]);
			}
		}
}

ll go_up(int deep,int x,ll &min_val)	//min_val记录u->lca中的最小值,同时也会发挥其他作用
{
	ll ans=0;
	for(int i=20;i>=0;--i)
	{
		if((1<<i)&deep)
		{
			ans = max(ans,up[x][i]);
			ans = max(ans,ma[x][i] - min_val);	//有点难讲,自行理解
			min_val = min(min_val,mi[x][i]);
			x=fa[x][i];
		}
	}
	return ans;
}

ll go_down(int deep,int x,ll &max_val)	//max_val记录lca->v中的最大值,同样也会发挥其他作用
{
	ll ans=0;
	for(int i=20;i>=0;--i)
	{
		if((1<<i)&deep)
		{
			ans = max(ans,down[x][i]);
			ans = max(ans,max_val-mi[x][i]);	//自行理解
			max_val = max(max_val,ma[x][i]);
			x=fa[x][i];
		}
	}
	return ans;
}

int LCA(int u,int v,int n)
{
	if(depth[v]>depth[u])	swap(u,v);
	int temp=depth[u]-depth[v];
	for(int i=0;(1<<i)<=temp;++i)
	{
		if((1<<i)&temp)
			u=fa[u][i];
	}
	if(u==v)	return u;
	int k=0;
	while((1<<(k+1))<=n)	++k;
	for(int i=k;i>=0;--i)
	{
		if(fa[u][i]!=fa[v][i])
		{
			u=fa[u][i];	
			v=fa[v][i];
		}
	}
	return fa[u][0];
}

int main()
{
	ll n;
	read(n);
	for(int i=1;i<=n;++i)	read(w[i]);
	for(int i=1;i<n;++i)
	{
		ll u,v;
		read(u);	read(v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	init(n);
	ll q;
	read(q);
	while(q--)
	{
		ll u,v;
		read(u);	read(v);
		ll ans,ma1,mi1;
		int lca=LCA(u,v,n);
		ma1=0;	mi1=inf;
		ans=max(0ll,max(go_up(depth[u]-depth[lca],u,mi1),go_down(depth[v]-depth[lca],v,ma1)));
		ans=max(ans,ma1-mi1);
		out(ans);	putchar('\n');
	}
	return 0;
}

PS:分治思想有时可以发挥很大的作用。


http://www.niftyadmin.cn/n/942921.html

相关文章

bzoj 2006: [NOI2010]超级钢琴

Description 小Z是一个小有名气的钢琴家&#xff0c;最近C博士送给了小Z一架超级钢琴&#xff0c;小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符&#xff0c;编号为1至n。第i个音符的美妙度为Ai&#xff0c;其中Ai可正可负。 一个“超级和弦…

2019 ICPC Malaysia National,E. Optimal Slots(01背包变形)

大致题意&#xff1a;给定T和N个数&#xff0c;在N个数当中选出一些数相加&#xff0c;得到sum&#xff0c;你的任务就是要使sum<T且T-sum要尽可能小&#xff0c;最后按照编号顺序输出你的方案&#xff0c;最后再输出sum。如果有多个方案&#xff0c;编号小的优先。 限制条件…

bzoj 3784: 树上的路径

Description 给定一个N个结点的树&#xff0c;结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b&#xff09;表示从结点a到结点b路边上经过边的权值。其中要求a<b.将这n*(n-1)/2个距离从大到小排序&#xff0c;输出前M个距离值。Input 第一行两个正整数N,M下面N-1行&…

洛谷P1638 逛画展(双向队列)

在算法标签中搜了单调队列&#xff0c;搜到了这道题&#xff0c;但实际做起来发现并不是个单调队列&#xff0c;只是个双向队列的应用罢了… 朴素算法是暴力枚举&#xff0c;时间复杂度是O(n)&#xff0c;用双向队列优化后可以降为O(n)。 首先我们设置num[i]&#xff0c;表示第…

bzoj 2718: [Violet 4]毕业旅行

Description Input Output 最多可选多少景点 Sample Input 7 6 1 2 2 3 5 4 4 3 3 6 6 7 Sample Output 2 HINT Source Ctsc2008 River & ural 1533. Fat Hobbits 首先有个结论。。最小路径覆盖n-最大二分图匹配然后这题就没了。。。记得floyd处理下联通刚好练了一下网络流…

bzoj 1051: [HAOI2006]受欢迎的牛

Description 每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛&#xff0c;给你M对整数(A,B)&#xff0c;表示牛A认为牛B受欢迎。 这种关系是具有传递性的&#xff0c;如果A认为B受欢迎&#xff0c;B认为C受欢迎&#xff0c;那么牛A也认为牛C受欢迎。你的任务是求出有多少…

The Preliminary Contest for ICPC Asia Xuzhou 2019,E(思维)

大致题意&#xff1a;有n个人&#xff0c;有n个数a[n]&#xff0c;a[i]表示第i个人的能力值&#xff0c;给定一个m&#xff0c;m为buff值&#xff0c;现在问你对于第i人(1<i<n)人的能力值加上buff后&#xff0c;找到最右边的能力值大于他的人的位置j&#xff0c;然后输出…

bzoj 3993: [Sdoi2015]星际战争

Description 3333年&#xff0c;在银河系的某星球上&#xff0c;X军团和Y军团正在激烈地作战。在战斗的某一阶段&#xff0c;Y军团一共派遣了N个巨型机器人进攻X军团的阵地&#xff0c;其中第i个巨型机器人的装甲值为Ai。当一个巨型机器人的装甲值减少到0或者以下时&#xff0c…