集训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原题,因为,数据范围不一样