• 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
    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] = 1;  
        for(int i = 0; i < ask[u].size(); i++) {  
            int v = ask[u][i];  
            if(vis[v]) {  
                int t = find(v);  
                int z = pos[u][i];  
                if(z > 0) {  
                    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 = 0; i < g[u].size(); i++) {  
            int v = g[u][i];  
            if(!vis[v]) {  
                tarjan(v);  
                fa[v] = u;  
            }  
        }  
        for(int i = 0; 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 = 1; i <= n; i++) {  
                scanf("%d", &w);  
                mx[i] = mi[i] = w; fa[i] = i;  
            }  
            for(int i = 1; i < n; i++) {  
                scanf("%d%d", &u, &v);  
                g[u].push_back(v);  
                g[v].push_back(u);  
      
            }  
            scanf("%d", &Q);  
            for(int i = 1; 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(1);  
            for(int i = 1; i <= Q; i++) printf("%d
    ", ans[i]);  
      
        return 0;  
    }  
    #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=50000+3;
    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=0;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=0;i<G[u].size();++i)
        {
            int v=G[u][i];
            if(!vis[v])
            {
                tarjan(v);
                par[v]=u;
            }
        }
        for(int i=0;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<0)
            {
                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=1;i<=V;++i)
        {
            scanf("%d", &val[i]);
            max_val[i]=min_val[i]=val[i];
        }
        for(int i=1;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=1;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(1);
        for(int i=1;i<=Q;++i)
            printf("%d
    ", res[i]);
    
        return 0;
    }
    View Code

    倍增写法

    #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=50000+3;
    const int MAXLOG=17;
    
    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[0][u]=fa;
        depth[u]=deep;
        dp_up[0][u]=max(price[fa]-price[u], 0);
        dp_down[0][u]=max(price[u]-price[fa], 0);
        dp_max[0][u]=max(price[u], price[fa]);
        dp_min[0][u]=min(price[u], price[fa]);
        for(int i=0;i<G[u].size();++i)
            if(G[u][i]!=fa)
                dfs(G[u][i], u, deep+1);
    }
    
    void pre_work()
    {
        mem(dp_max, 0);
        mem(dp_min, 0x3f);
        dfs(0, -1, 0);
        for(int k=0;k+1<MAXLOG;++k)
        {
            for(int u=0;u<N;++u)
            {
                if(parent[k][u]<0)
                    parent[k+1][u]=-1;
                else
                {
                    parent[k+1][u]=parent[k][parent[k][u]];
                    int mid=parent[k][u];
                    dp_max[k+1][u]=max(dp_max[k][u], dp_max[k][mid]);
                    dp_min[k+1][u]=min(dp_min[k][u], dp_min[k][mid]);
                    dp_up[k+1][u]=max(max(dp_up[k][u], dp_up[k][mid]), dp_max[k][mid]-dp_min[k][u]);
                    dp_down[k+1][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=0;k<MAXLOG;++k)
            if((depth[v]-depth[u])>>k&1)
                v=parent[k][v];
        if(u==v)
            return u;
        for(int k=MAXLOG-1;k>=0;--k)
            if(parent[k][u]!=parent[k][v])
            {
                u=parent[k][u];
                v=parent[k][v];
            }
        return parent[0][u];
    }
    
    
    int up(int u, int k, int &the_min)
    {
        the_min=INF;
        int res=0, pre_min_price=INF;
        for(int i=MAXLOG-1;i>=0;--i)
            if(k>>i&1)
            {
                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=0;
        int res=0, pre_max_price=0;
        for(int i=MAXLOG-1;i>=0;--i)
            if(k>>i&1)
            {
                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=0;i<N;++i)
            scanf("%d", &price[i]);
        for(int i=1;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
    ", max(max(up_profit, down_profit), the_max-the_min));
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    常用问题处理
    modelsim仿真中遇到的问题
    modelsim编译altera的库
    verilog系统函数用法
    123
    MATLAB实现截位的问题
    加减与有符号和无符号库
    状态机中的RAM注意的问题--减少扇出的办法
    串口的时序
    L230 RF可靠性测试-RF指标
  • 原文地址:https://www.cnblogs.com/caiyishuai/p/8605898.html
Copyright © 2020-2023  润新知