POJ 3728 The merchant(LCA+DP)
The merchant
Time Limit : 6000/3000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other)
Total Submission(s) : 1 Accepted Submission(s) : 1
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.
<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>
<i>Q</i> lines, each contains the maximum profit of the
corresponding path. If no positive profit can be earned, output 0 instead.
</p>
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
2
2
0
0
0
0
2
0
题目大意
有一棵树,每个结点有一个物品的价值。有一些询问,问在从一个点到另一个点了路径上,先在一个地方买,再在一个地方卖的最大获利。
解题思路
令从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)的更多相关文章
- POJ 3728 The merchant (树形DP+LCA)
题目:https://vjudge.net/contest/323605#problem/E 题意:一棵n个点的树,然后有m个查询,每次查询找(u->v)路径上的两个数,a[i],a[j],(i ...
- poj 3728 The merchant(LCA)
Description There are N cities in a country, and there is one and only one simple path between each ...
- POJ 3278:The merchant(LCA&DP)
The merchant Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 6864 Accepted: 2375 Desc ...
- poj 3728 The merchant 倍增lca求dp
题目: zdf给出的题目翻译: 从前有一个富饶的国度,在这里人们可以进行自由的交易.这个国度形成一个n个点的无向图,每个点表示一个城市,并且有一个权值w[i],表示这个城市出售或收购这个权值的物品.又 ...
- POJ 3728 The merchant(并查集+DFS)
[题目链接] http://poj.org/problem?id=3728 [题目大意] 给出一棵树,每个点上都可以交易货物,现在给出某货物在不同点的价格, 问从u到v的路程中,只允许做一次买入和一次 ...
- 【POJ 2152】 Fire (树形DP)
Fire Description Country Z has N cities, which are numbered from 1 to N. Cities are connected by h ...
- 树的点分治 (poj 1741, 1655(树形dp))
poj 1655:http://poj.org/problem?id=1655 题意: 给无根树, 找出以一节点为根, 使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子 ...
- POJ 2342 Anniversary party(树形dp)
Anniversary party Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7230 Accepted: 4162 ...
- POJ 1015 Jury Compromise(双塔dp)
Jury Compromise Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 33737 Accepted: 9109 ...
随机推荐
- python实现免密码登录lunx服务器
import paramikoimport oshostname='192.168.76.10'username='root'# password='123456'ssh=paramiko.SSHCl ...
- SpringBoot2.0之整合Kafka
maven依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www. ...
- Centos7 docker容器 搭建
Dockerfile 文件如下: # # MAINTAINER Carson,C.J.Zeong <zcy@nicescale.com> # DOCKER-VERSION # # Dock ...
- EF与手写Model的区别以及联系
1.在数据库表名上,EF是随意的,但是如果是Model的话,就应该在建立数据库的时候考虑到讲数据库表名变为复数,如Movie.cs 数据库应该为Movies
- 分布式技术 memcached
memcached 是一个高性能的分布式内存对象缓存系统,用于动态web应用,以减轻数据库负载,它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.memcache ...
- spring mvc 官方下载
1.进入https://spring.io 2.选择projects选项卡 3.点击spring frawewoek 4.选择右中方的Reference 5.选择2.3章节 6.点击 Distribu ...
- devstack apache2/keystone 没有启动
在devstack中./rejoin-stack.sh 发现apache2/keystone 没有启动 单单手动启动apach2服务之后keystone并没有启动 sudo service apach ...
- 《Advanced Bash-scripting Guide》学习(六):从/etc/fstab中读行
本文所选的例子来自于<Advanced Bash-scripting Gudie>一书,译者 杨春敏 黄毅 ABS书上的例子: 代码块和I/O重定向 #!/bin/bash #从/etc/ ...
- python中sort()方法的cmp参数
<python基础编程>里有讲到一段高级排序: “如果希望元素能按照特定的方式进行排序(而不是sort函数默认的方式,即根据python的默认排序规则按升序排列元素,第5章内对此进行讲解) ...
- 转 postfix邮件服务下mailq、postmap、postqueue 、 postsuper等用法
1.Mailq 功能说明:显示待寄邮件的清单. 语 法:mailq [-q] 补充说明:mailq可列出待寄邮件的清单,包括邮件ID,邮件大小,邮件保存时间,寄信人,收信人,以及邮件无法寄出的原因,提 ...