The merchant

Time Limit : 6000/3000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 1   Accepted Submission(s) : 1
Problem Description

There are N cities in a country, and there is one and only one simple path between each pair of cities. A merchant has chosen some paths and wants to earn as much money as possible in each path. When he move along a path, he can choose one city to buy some goods and sell them in a city after it. The goods in all cities are the same but the prices are different. Now your task is to calculate the maximum possible profit on each path.

 
Input
<div><p>The first line contains
<i>N</i>, the number of cities.<br>Each of the next
<i>N</i> lines contains <i>w<sub>i</sub></i>
the goods' price in each city.<br>Each of the next <i>N-1</i>
lines contains labels of two cities, describing a road between the two
cities.<br>The next line contains <i>Q</i>, the number of
paths.<br>Each of the next <i>Q</i> lines contains labels of
two cities, describing a path. The cities are numbered from 1 to
<i>N</i>. </p><p>1 ≤ <i>N</i>,
<i>w<sub>i</sub></i>, <i>Q</i> ≤ 50000
<br></p>
 
Output
<div><p>The output contains
<i>Q</i> lines, each contains the maximum profit of the
corresponding path. If no positive profit can be earned, output 0 instead.
</p>
 
Sample Input
4
1
5
3
2
1 3
3 2
3 4
9
1 2
1 3
1 4
2 3
2 1
2 4
3 1
3 2
3 4
 
Sample Output
4
2
2
0
0
0
0
2
0
 
Source
PKU
 

题目大意

  有一棵树,每个结点有一个物品的价值。有一些询问,问在从一个点到另一个点了路径上,先在一个地方买,再在一个地方卖的最大获利。

解题思路

  令从uu到vv的LCA为xx,那么答案一定是从uu到xx的最大获利,从xx到vv的最大获利,xx到vv的最大值减去uu到xx的最小值。于是我们就可以在求LCA的过程中DP一下得到上面所需的东西。 
  用离线Tarjan实现时在并查集路径压缩时进行DP,用倍增实现时直接在倍增数组上进行DP。

离线Tarjan写法

用的方法是离线LCA,在上面加了一些东西

对于一个询问, 假设u,v的LCA是f

那么有三种可能, 一个是从u到f 买卖了。 一个是从f到v买卖了,  一个是从u到f之间买了,从v到f卖了

从u到f 我们称为up,  从f到v我们称为down,而从u到f买然后在f到v卖就是求u到f的最小值和f到v的最大值。

我们要分别求这三个值

实际上就是在离线tarjan求LCA的过程中求出的

对于每个点, 我们进行dfs的时候,查看于其相关的询问,

假设当前点是u, 询问的点是v, u和v的LCA是f,如果v已经dfs过了,说明v在并查集中的祖先就是u,v的LCA  f点,

将该询问加入到f的相关集合中,等f所有的子节点都处理过后再去处理f, 就可以发现,一切都是顺其自然了

在这些处理过程中,up和down以及u,v到f的最大值最小值  都可以在并查集求压缩路径的过程中更新。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
#include <ctime>
#define MAXN 52222
#define MAXM 222222
#define INF 1000000001
using namespace std;
vector<int>g[MAXN], st[MAXN], ed[MAXN], id[MAXN], ask[MAXN], pos[MAXN];
int mx[MAXN], mi[MAXN], up[MAXN], down[MAXN], vis[MAXN], fa[MAXN], ans[MAXN];
int n, Q;
int find(int x) {
if(x == fa[x]) return x;
int y = fa[x];
fa[x] = find(y);
up[x] = max(up[x], max(mx[y] - mi[x], up[y]));
down[x] = max(down[x], max(mx[x] - mi[y], down[y]));
mx[x] = max(mx[x], mx[y]);
mi[x] = min(mi[x], mi[y]);
return fa[x];
}
void tarjan(int u) {
vis[u] = ;
for(int i = ; i < ask[u].size(); i++) {
int v = ask[u][i];
if(vis[v]) {
int t = find(v);
int z = pos[u][i];
if(z > ) {
st[t].push_back(u);
ed[t].push_back(v);
id[t].push_back(z);
} else {
st[t].push_back(v);
ed[t].push_back(u);
id[t].push_back(-z);
}
}
}
for(int i = ; i < g[u].size(); i++) {
int v = g[u][i];
if(!vis[v]) {
tarjan(v);
fa[v] = u;
}
}
for(int i = ; i < st[u].size(); i++) {
int a = st[u][i];
int b = ed[u][i];
int t = id[u][i];
find(a);
find(b);
ans[t] = max(up[a], max(down[b], mx[b] - mi[a]));
}
} int main() {
scanf("%d", &n);
int u, v, w; for(int i = ; i <= n; i++) {
scanf("%d", &w);
mx[i] = mi[i] = w; fa[i] = i;
}
for(int i = ; i < n; i++) {
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u); }
scanf("%d", &Q);
for(int i = ; i <= Q; i++) {
scanf("%d%d", &u, &v);
ask[u].push_back(v);
pos[u].push_back(i);
ask[v].push_back(u);
pos[v].push_back(-i);
}
tarjan();
for(int i = ; i <= Q; i++) printf("%d\n", ans[i]); return ;
}
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <cstdlib>
using namespace std;
#define INF 0x3f3f3f3f
#define fi first
#define se second const int MAXN=+;
int V, Q, val[MAXN];
vector<int> G[MAXN];
vector<pair<int, int> > q[MAXN];// id, other
vector<pair<int, int> > ans[MAXN];// to, it
int up[MAXN], down[MAXN];//当前点到lca的最大获利,lca到当前点点最大获利
int max_val[MAXN], min_val[MAXN];//当前点到lca的最大/最小价格
int par[MAXN];
bool vis[MAXN];
int res[MAXN]; int findfather(int x)//并查集查询,同时进行dp
{
if(par[x]==x)
return x;
int fa=par[x];
par[x]=findfather(par[x]);
up[x]=max(up[x], max(up[fa], max_val[fa]-min_val[x]));
down[x]=max(down[x], max(down[fa], max_val[x]-min_val[fa]));
max_val[x]=max(max_val[x], max_val[fa]);
min_val[x]=min(min_val[x], min_val[fa]);
return par[x];
} void tarjan(int u)
{
par[u]=u;
vis[u]=true;
for(int i=;i<q[u].size();++i)//把查询保存到lca处
{
int v=q[u][i].se;
if(vis[v])
{
int lca=findfather(v);
ans[lca].push_back(make_pair(u, i));
}
}
for(int i=;i<G[u].size();++i)
{
int v=G[u][i];
if(!vis[v])
{
tarjan(v);
par[v]=u;
}
}
for(int i=;i<ans[u].size();++i)//处理以当前结点为lca的所有查询
{
int x=ans[u][i].fi, y=q[x][ans[u][i].se].se;
int id=q[x][ans[u][i].se].fi;
if(id<)
{
id=-id;
swap(x, y);
}
findfather(x);
findfather(y);
res[id]=max(up[x], down[y]);
res[id]=max(res[id], max_val[y]-min_val[x]);
}
} int main()
{
scanf("%d", &V);
for(int i=;i<=V;++i)
{
scanf("%d", &val[i]);
max_val[i]=min_val[i]=val[i];
}
for(int i=;i<V;++i)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
scanf("%d", &Q);
for(int i=;i<=Q;++i)
{
int u, v;
scanf("%d%d", &u, &v);
q[u].push_back(make_pair(i, v));
q[v].push_back(make_pair(-i, u));
}
tarjan();
for(int i=;i<=Q;++i)
printf("%d\n", res[i]); return ;
}

倍增写法

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <ctime>
#include <bitset>
using namespace std;
#define INF 0x3f3f3f3f
#define ULL unsigned long long
#define LL long long
#define fi first
#define se second
#define mem(a, b) memset((a),(b),sizeof(a))
#define sqr(x) ((x)*(x)) const int MAXN=+;
const int MAXLOG=; int N, Q, price[MAXN];
vector<int> G[MAXN];
int dp_max[MAXLOG][MAXN], dp_min[MAXLOG][MAXN];//向上走2^k步之间的最高与最低价格
int dp_up[MAXLOG][MAXN], dp_down[MAXLOG][MAXN];//从u向上走2^k/向下走2^k步到u 的最大利润
int parent[MAXLOG][MAXN];//向上走2^k步到达的点(超过根时记为-1)
int depth[MAXN]; void dfs(int u, int fa, int deep)
{
parent[][u]=fa;
depth[u]=deep;
dp_up[][u]=max(price[fa]-price[u], );
dp_down[][u]=max(price[u]-price[fa], );
dp_max[][u]=max(price[u], price[fa]);
dp_min[][u]=min(price[u], price[fa]);
for(int i=;i<G[u].size();++i)
if(G[u][i]!=fa)
dfs(G[u][i], u, deep+);
} void pre_work()
{
mem(dp_max, );
mem(dp_min, 0x3f);
dfs(, -, );
for(int k=;k+<MAXLOG;++k)
{
for(int u=;u<N;++u)
{
if(parent[k][u]<)
parent[k+][u]=-;
else
{
parent[k+][u]=parent[k][parent[k][u]];
int mid=parent[k][u];
dp_max[k+][u]=max(dp_max[k][u], dp_max[k][mid]);
dp_min[k+][u]=min(dp_min[k][u], dp_min[k][mid]);
dp_up[k+][u]=max(max(dp_up[k][u], dp_up[k][mid]), dp_max[k][mid]-dp_min[k][u]);
dp_down[k+][u]=max(max(dp_down[k][u], dp_down[k][mid]), dp_max[k][u]-dp_min[k][mid]);
}
}
}
} int lca(int u, int v)
{
if(depth[u]>depth[v])
swap(u, v);
for(int k=;k<MAXLOG;++k)
if((depth[v]-depth[u])>>k&)
v=parent[k][v];
if(u==v)
return u;
for(int k=MAXLOG-;k>=;--k)
if(parent[k][u]!=parent[k][v])
{
u=parent[k][u];
v=parent[k][v];
}
return parent[][u];
} int up(int u, int k, int &the_min)
{
the_min=INF;
int res=, pre_min_price=INF;
for(int i=MAXLOG-;i>=;--i)
if(k>>i&)
{
the_min=min(the_min, dp_min[i][u]);
res=max(res, dp_up[i][u]);
res=max(res, dp_max[i][u]-pre_min_price);
pre_min_price=min(pre_min_price, dp_min[i][u]);
u=parent[i][u];
}
return res;
} int down(int u, int k, int &the_max)
{
the_max=;
int res=, pre_max_price=;
for(int i=MAXLOG-;i>=;--i)
if(k>>i&)
{
the_max=max(the_max, dp_max[i][u]);
res=max(res, dp_down[i][u]);
res=max(res, pre_max_price-dp_min[i][u]);
pre_max_price=max(pre_max_price, dp_max[i][u]);
u=parent[i][u];
}
return res;
} int main()
{
scanf("%d", &N);
for(int i=;i<N;++i)
scanf("%d", &price[i]);
for(int i=;i<N;++i)
{
int u, v;
scanf("%d%d", &u, &v);
--u;
--v;
G[u].push_back(v);
G[v].push_back(u);
}
pre_work();
scanf("%d", &Q);
while(Q--)
{
int u, v;
scanf("%d%d", &u, &v);
--u;
--v;
int com=lca(u, v);//最近公共祖先
int the_max, the_min;
int up_profit=up(u, depth[u]-depth[com], the_min);
int down_profit=down(v, depth[v]-depth[com], the_max);
printf("%d\n", max(max(up_profit, down_profit), the_max-the_min));
} return ;
}

POJ 3728 The merchant(LCA+DP)的更多相关文章

  1. POJ 3728 The merchant (树形DP+LCA)

    题目:https://vjudge.net/contest/323605#problem/E 题意:一棵n个点的树,然后有m个查询,每次查询找(u->v)路径上的两个数,a[i],a[j],(i ...

  2. poj 3728 The merchant(LCA)

    Description There are N cities in a country, and there is one and only one simple path between each ...

  3. POJ 3278:The merchant(LCA&DP)

    The merchant Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6864   Accepted: 2375 Desc ...

  4. poj 3728 The merchant 倍增lca求dp

    题目: zdf给出的题目翻译: 从前有一个富饶的国度,在这里人们可以进行自由的交易.这个国度形成一个n个点的无向图,每个点表示一个城市,并且有一个权值w[i],表示这个城市出售或收购这个权值的物品.又 ...

  5. POJ 3728 The merchant(并查集+DFS)

    [题目链接] http://poj.org/problem?id=3728 [题目大意] 给出一棵树,每个点上都可以交易货物,现在给出某货物在不同点的价格, 问从u到v的路程中,只允许做一次买入和一次 ...

  6. 【POJ 2152】 Fire (树形DP)

    Fire   Description Country Z has N cities, which are numbered from 1 to N. Cities are connected by h ...

  7. 树的点分治 (poj 1741, 1655(树形dp))

    poj 1655:http://poj.org/problem?id=1655 题意: 给无根树,  找出以一节点为根,  使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子 ...

  8. POJ 2342 Anniversary party(树形dp)

    Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7230   Accepted: 4162 ...

  9. POJ 1015 Jury Compromise(双塔dp)

    Jury Compromise Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 33737   Accepted: 9109 ...

随机推荐

  1. linux 虚拟机在线添加新磁盘

    在线添加磁盘,扩展LVM卷案例   一.添加硬盘,在线扫描出来 首先到虚拟机那里添加一块硬盘,注意必须是SCSI类型的硬盘. 扫描硬盘,不用重启操作系统的. echo "- - -" ...

  2. C++中substr的用法

    C++中substr函数的用法 #include<string> #include<iostream> using namespace std; void main() { s ...

  3. MapReduce-排序(全部排序、辅助排序)

    排序 排序是MapReduce的核心技术. 1.准备 示例:按照气温字段对天气数据集排序.由于气温字段是有符号的整数,所以不能将该字段视为Text对象并以字典顺序排序.反之,用顺序文件存储数据,其In ...

  4. matlab学习笔记,图像分块

    clc; clear all; close all; I = imread('E:\matlab\files-images\tomsen512.jpg'); rs = size(I, 1);% 行数c ...

  5. Visual Studio中用于ASP.NET Web项目的Web服务器

    当您在 Visual Studio 中开发 Web 项目时,需要 Web 服务器才能测试或运行它们. 利用 Visual Studio,您可以使用不同的 Web 服务器进行测试,包括 IIS Expr ...

  6. ural 2015 Zhenya moves from the dormitory(模拟)

    2015. Zhenya moves from the dormitory Time limit: 1.0 secondMemory limit: 64 MB After moving from hi ...

  7. java中base64

    // 将 s 进行 BASE64 编码 public static String getBASE64(String s) { if (s == null) return null; return (n ...

  8. codeigniter教程:Codeigniter出现Unable to connect to your databas

    Codeigniter出现A Database Error Occurred错误 下午把项目构建到服务器上的时候,codeigniter竟然出现了 a database error occurred ...

  9. ps6-图层基础与操作技巧

    1.图层的新建.复制与删除 ctrl+j:复制图层,可以用复制选区作为新图层 Shift+Ctrl+Alt+e:在新的空白图层将下面所有的图层合并为一个图层. 2.选择复制与链接图层 在移动图层时,按 ...

  10. hdoj-1038-Biker's Trip Odometer(水题)

    题目真的考验英语 题目链接 需要进行单位的转换 对于Pi用:3.1415927. 5280步相当于1英里. 12英寸相当于1步. 60分钟等于1小时 60秒等于1分钟. 201.168米等于1弗朗.( ...