集训day15 t1 poj3728

【问题描述】

有一颗n个节点的树

每个节点上都有许多奸商在卖东西,第i个奸商的理想价格为vi,即他会以vi的价格购买或卖出一件东西

有m个人希望从树上的某个点走到另一个点,问你在只进行一次买卖(每次仅限一个商品)的情况下,每个人最多能赚多少钱

【输入】

输入第一行是一个整数 n,表示树上的点数。

接下来n个正整数,表示每个奸商的理想价格。

接下来n-1行,每行两个整数x,y,表示第x点和第y点有一条边。

接下来一个整数m,表示下来有m个询问。

接下来有m行,每行两个整数x和y,表示某个人要从第x点出发到第y点。

【输出】

输出包括m行。

每行对应一个询问,一个整数,表示此人最多能赚到多少钱。

【输入输出样例1】

10

3 4 1 2 7 6 1 5 3 9

1 2

1 9

3 1

9 7

5 9

6 9

8 7

4 7

10 7

3

5 6

8 10

2 4

3

8

1

【数据范围】

对于前40%数据 n<=1000, m<=1000

对于100%数据 n<=250000 ,m<=10000

 

t1
离线lca+dp思想

 

先说一说题外话,我后来看我自己的代码时,我看了差不多半天,也没有看懂。

最后总算是理解了代码的思想。


我们假设一个中间节点z
一件商品,假设u和v的lca是z
那么对于一次买卖,有三种情况:
一件商品,我们在u,f之间买卖了,在u,f之间买了,在f,v之间卖了,在f,v之间买卖了
接着我们想,对于三种情况,基于贪心的思想,必然是这样几种情况:
1.在u,f间价格最小的地方买入,在u,f间价格最大的地方卖出,
2.在u,f间价格最小的地方买入,在f,v间价格最大的地方卖出
3.在f,v间价格最小的地方买入,在f,v间价格最大的地方卖出
也就是说,我们要维护4个量:
u,f间的最大和最小值,f,v间的最大和最小值
从u到f 我们称为up, 从f到v我们称为down,而从u到f买然后在f到v卖就是求u到f的最小值和f到v的最大值


先求出u,v的最近公共祖先f
再求出u到f再到v的最大利润值maxval
这个maxval有三种情况
1.可能是u到f的利润最大值
2.可能是f到v的利润最大值
3.可能是f到v的最大w[i] - u到f的最小w[i]
这样就是说,我们需要4个变量才能得到最终的利润最大值
up[u]表示u到f的利润最大值
down[v]表示f到v的利润最大值
maxw[u]表示u到f的最大w[i]
minw[u]表示u到f的最小w[i]

首先,我们可以知道的是,原来我们暴力写肯定就是先求出LCA,然后再从u和v到lca更新重新更新

所以我们的优化思路是在求LCA的同时对4个值进行更新

那么我接下来就要解决这个问题了,我们显然可以用到一些DP的思想,

我们上面已经开了4个数组

up[u]表示u到f的利润最大值
down[v]表示f到v的利润最大值
maxw[u]表示u到f的最大w[i]
minw[u]表示u到f的最小w[i]

然后我们在利用并查集路径压缩的同时更新这些值,现在我们要解决的是,如何能按照求lca的次序,同时回答询问

这就是说我们要给询问排一个序号,按照求lca得顺序完成询问,然后通过序号,把答案确定在答案数组的正确位置。

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

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

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

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

同时我们注意到,

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 250086;
  4 const int maxm = 10086;
  5 struct kkk {
  6     int net, y, id;
  7 }ef[maxn << 1], es[maxn << 1], et[maxn << 1];
  8 int linf[maxn], lenf = 0;
  9 int lins[maxn], lens = 0;
 10 int lint[maxn], lent = 0;
 11 int n, m, w[maxn];
 12 int fa[maxn], u[maxm], v[maxm];
 13 int up[maxn], down[maxn];
 14 int maxw[maxn], minw[maxn];
 15 int ans[maxn];
 16 int vis[maxn];
 17 
 18 inline int read() {
 19     int x = 0, y = 1;
 20     char ch = getchar();
 21     while(!isdigit(ch)) {
 22         if(ch == '-') y = -1;
 23         ch = getchar();
 24     }
 25     while(isdigit(ch)) {
 26         x = (x << 1) + (x << 3) + ch - '0';
 27         ch = getchar();
 28     }
 29     return x * y;
 30 }
 31 
 32 inline void insert_f(int xx, int yy, int id) {
 33     ef[++lenf].net = linf[xx];
 34     ef[lenf].id = id;
 35     ef[lenf].y = yy;
 36     linf[xx] = lenf;
 37 }
 38 
 39 inline void insert_s(int xx, int yy, int id) {
 40     es[++lens].id = id;
 41     es[lens].net = lins[xx];
 42     es[lens].y = yy;
 43     lins[xx] = lens;
 44 }
 45 
 46 inline void insert_t(int xx, int yy, int id) {
 47     et[++lent].id = id;
 48     et[lent].net = lint[xx];
 49     et[lent].y = yy;
 50     lint[xx] = lent;
 51 }
 52 
 53 inline int getfather(int x) {
 54     if(x == fa[x]) return x;
 55     int f = fa[x];
 56     fa[x] = getfather(fa[x]);
 57     up[x] = max(max(up[x], up[f]), maxw[f] - minw[x]);
 58     down[x] = max(max(down[x], down[f]), maxw[x] - minw[f]);
 59     maxw[x] = max(maxw[x], maxw[f]);
 60     minw[x] = min(minw[x], minw[f]);
 61     return fa[x];
 62 }
 63 
 64 inline void LCA(int x) {//x表示的是公共祖先 
 65     vis[x] = 1;
 66     fa[x] = x;
 67     for(int i = lins[x]; i; i = es[i].net) {
 68         int to = es[i].y, id = es[i].id;
 69         if(!vis[to]) continue;
 70         int dad = getfather(to);
 71         insert_t(dad, x, id); 
 72     }
 73     for(int i = linf[x]; i; i = ef[i].net) {
 74         int to = ef[i].y;
 75         if(vis[to]) continue;
 76         LCA(to);
 77         fa[to] = x;//把to的父亲标记为他的父亲 
 78     }
 79     for(int i = lint[x]; i; i = et[i].net) {//在父亲节点x处理所有的关于他的子节点的询问
 80         int id = et[i].id;
 81         getfather(u[id]);
 82         getfather(v[id]);
 83         ans[id] = max(max(up[u[id]], down[v[id]]), maxw[v[id]] - minw[u[id]]);
 84     }
 85 }
 86 
 87 int main() {
 88 //    freopen("gift.in", "r", stdin);
 89 //    freopen("gift.out", "w", stdout);
 90     n = read();
 91     for(int i = 1; i <= n; ++i) {
 92         w[i] = read();
 93         up[i] = down[i] = 0;
 94         maxw[i] = minw[i] = w[i];
 95     }
 96     for(int i = 1; i < n; ++i) {
 97         int x, y;
 98         x = read(), y = read();
 99         insert_f(x, y, i);
100         insert_f(y, x, i);
101     }
102     m = read();
103     for(int i = 1; i <= m; ++i) {
104         u[i] = read(), v[i] = read();
105         insert_s(u[i], v[i], i);
106         insert_s(v[i], u[i], i);
107     }
108     LCA(1);
109     for(int i = 1; i <= m; ++i) {
110         if(ans[i] < 0) cout << 0 << '\n';
111         else cout << ans[i] << '\n';
112     }
113 //    fclose(stdin);
114 //    fclose(stdout);
115     return 0;
116 }

 但是此代码并不能A掉Poj原题,因为,数据范围不一样

posted @ 2018-07-19 14:20  YuWenjue  阅读(167)  评论(0编辑  收藏  举报