在平面直角坐标系中,在 x 负半轴有一些位置,编号 1 ~ n ,正半轴有 m 个点 (si,0) 。每个点有一个移动向量 (xi,yi) 每一秒按此向量移动 (0≤xi,yi≤105) ,每个点的价值为它到编号为 [li,ri] 区间内位置的曼哈顿距离和,q 个询问,每个询问给定 t ,求 y 时刻每个点价值的最小值。
signedmain(){ fastin >> n >> m >> q; for (int i = 1; i <= n; i++) { fastin >> s[i] >> l[i] >> r[i] >> x[i] >> y[i]; } for (int i = 1; i <= m; i++) { fastin >> c[i]; c[i] = -c[i]; sum[i] = sum[i - 1] + c[i]; } for (int i = 1; i <= n; i++) { line[i].b = sum[r[i]] - sum[l[i] - 1] + (r[i] - l[i] + 1) * s[i]; line[i].k = (x[i] + y[i]) * (r[i] - l[i] + 1); } sort(line + 1, line + n + 1); cnt = unique(line + 1, line + n + 1) - line - 1; int st = 0; line[0].b = inf; for (int i = 1; i <= cnt; i++) { if (line[i].b < line[st].b) { st = i; } if (line[i].b == line[st].b && line[i].k < line[st].k) { st = i; } } pos[1] = 0; pid[1] = st; int top = 1; for (int i = st + 1; i <= cnt; i++) { while (cross_point(pid[top], i) <= pos[top]) { top--; } pos[top + 1] = cross_point(pid[top], i); pid[top + 1] = i; top++; } for (int i = 1, t; i <= q; i++) { fastin >> t; int p = upper_bound(pos + 1, pos + top + 1, t) - pos - 1; printf("%lld\n", value(pid[p], t)); } return0; }
overwatch
给一颗 n 个点的树,点有权值 ai,定义一条链的值为链上所有点权值和。
q 个询问,每个询问两个数 w,k。改变一个点的权值为 w ,求每次改变后的树中长度为 k (点的个数为 k) 的链的最大值。
n,q≤105,k≤5
k 的大小不超过 5 ,这是一个很好的入手条件。
发现对于点 x 的每个儿子,里面可能有很多长度为 k 的小路径可以接上点 x 变成一条长度为 k+1 的小路径,但是我们只需要那条权值最大的就行了。所以对于每个点,我们开 k 个 set (每个长度开一个),每个 set 的大小为这个点的孩子个数,把每个儿子当前长度权值最大的那条小路径塞进去,然后计算答案的话拼一拼就好了 (还是要特判来自同一子树)。
对于一次修改 (x,w), 只会影响到点 x 以及 Ta 的 1…(k−1) 级祖先,我们先把原来最长的小路径删去,然后再把现在新的最长的小路径加上就好了,对于每个被修改的点,以 Ta 为 lca 的大路径的答案可能发生变化,所以我们可以在外层写一个可删堆,里面塞上以每个点为 lca 的大路径的答案。那么现在对于每个被修改的点,只要把原先在外层可删堆里的答案删掉,再重新拼一拼计算出新的答案,塞回外层可删堆里就好了。