【题目描述】
风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。
由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1给出的树包含 8 个顶点、7 条边)。这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),权值为c_i。接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 u_i 到顶点v_i 的路径。幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从b到 a的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?
【输入】
第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。
接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点按1到 n标号。
接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其中0≤c≤10^9,a不等于b。
接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,第k 小一定存在。
【输出】
对于每个果子,输出一行表示选择的盘子的权值。
【输入样例】
10 10 10 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 3 2 217394434 10 7 13022269 6 7 283254485 6 8 333042360 4 6 442139372 8 3 225045590 10 4 922205209 10 8 808296330 9 2 486331361 4 9 551176338 1 8 5 3 8 3 3 8 4 1 8 3 4 8 1 2 3 1 2 3 1 2 3 1 2 4 1 1 4 1
【输出样例】
442139372 333042360 442139372 283254485 283254485 217394434 217394434 217394434 217394434 217394434
【提示】
N,P,Q≤40000。
一道经典的整体二分。
盘子能接到水果的条件是盘子是水果的子路径。
考虑一个盘子(u,v)可以接到哪些水果。
假设原树为有根树,那么有两种情况。(下文中LCA指最近公共祖先)
- lca(u,v)为u或v
- lca(u,v)不为u且不为v
对于第二种情况来说,相当于把水果的两个端点卡死在了u,v的子树内。
子树限制的问题就可以用dfs序。
为了方便我们规定一条路径用(x,y)表示,其中x的dfs序小于y。
这样相当于水果(x,y)种,x有一个取值范围,y有一个取值范围。
我们把盘子看成一个矩形,把水果看成一个点,那么被接到的条件等价于这个点落在矩形内。
问题就变成了对于每个点,求出覆盖它的权值第K大的矩形。
那么对于第一种情况怎么办呢?
现在已知(u,v)满足lca(u,v)=u (已经定义u的dfs序小于v)
设路径u->v上u之后的第一个点为t
我们发现只要满足一端在t的子树外,一端在v的子树内就可以了
为什么不是在u的子树外呢?画图可以发现,另一个端点在u的其它子树内其实也满足条件。
考虑我们规定了(u,v)的顺序,那么讨论一下u在t内还是v在t内就得到两个矩形。
这样落在其中任意一个就满足条件。
现在问题相当于对于每个点,求出覆盖它的权值第K大的图形(包括一个矩形、两个矩形)
这个就可以直接整体二分,二分一个权值,然后用扫描线加树状数组验证就行了。
整体二分写成扣除左边对右边的贡献比较好写。
居然1A了,真的高兴qwq
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 1e5 + 5; struct edge { int v, next; } e[maxn << 1]; int head[maxn], cnt; void adde(const int &u, const int &v) { e[++cnt] = (edge) {v, head[u]}; head[u] = cnt; } int dfn[maxn], dfned[maxn], dcnt, n, N, M, u, v, w; int tp[maxn], fa[maxn], son[maxn], sz[maxn], dep[maxn]; void dfs(int u, int p) { sz[u] = 1, fa[u] = p; for(register int i = head[u]; i; i = e[i].next) { int v = e[i].v; if(v == p) continue; dfs(v, u); sz[u] += sz[v]; if(!son[u] || sz[son[u]] < sz[v]) son[u] = v; } } void dfs2(int u, int top) { dfn[u] = ++dcnt; tp[u] = top, dep[u] = dep[fa[u]] + 1; if(son[u]) dfs2(son[u], top); for(register int i = head[u]; i; i = e[i].next) { int v = e[i].v; if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } dfned[u] = dcnt; } int LCA(int u, int v) { while(tp[u] != tp[v]) { if(dep[tp[u]] < dep[tp[v]]) swap(u, v); u = fa[tp[u]]; } return dep[u] > dep[v] ? v : u; } int Fdown(int p, int u) { while(tp[u] != tp[p]) { if(fa[tp[u]] == p) return tp[u]; u = fa[tp[u]]; } return son[p]; } struct Fruit {int x, y, k, id;} frts[maxn], ftmp[maxn]; struct Rect {int l, r, d, u;}; struct Plate { int t, w; Rect r1, r2; bool operator < (const Plate &A) const { return w < A.w; } } plts[maxn], ptmp[maxn]; struct Event { int x, d, u, t; bool operator < (const Event &A) const { return x < A.x; } } ev[maxn << 1]; struct Query { int x, y, id; bool operator < (const Query &A) const { return x < A.x; } } qs[maxn << 1]; int ans[maxn], tmp[maxn], ecnt, qcnt, ep; namespace BIT { int t[maxn]; #define lowbit(x) ((x) & (-(x))) void add(int x, int dt) { for(; x <= n; x += lowbit(x)) t[x] += dt; } void add(int l, int r, int dt) { add(l, dt), add(r + 1, -dt); } int query(int x) { int ans = 0; for(; x; x -= lowbit(x)) ans += t[x]; return ans; } } void solve(int pl, int pr, int fl, int fr, int L, int R) { if(L >= R - 1) { for(register int i = fl; i < fr; ++i) ans[frts[i].id] = L; return ; } if(fl == fr || pl == pr) return; int mid = L + R >> 1; int tmp, p1 = fl, p2 = fr - 1, p3 = pl, p4 = pr - 1; ecnt = qcnt = 0, ep = 1; for(register int i = fl; i < fr; ++i) { qs[++qcnt] = (Query) {frts[i].x, frts[i].y, i}; } for(register int i = pl; i < pr; ++i) { if(plts[i].w < mid) { ptmp[p3++] = plts[i]; ev[++ecnt] = (Event) {plts[i].r1.l, plts[i].r1.d, plts[i].r1.u, 1}; ev[++ecnt] = (Event) {plts[i].r1.r + 1, plts[i].r1.d, plts[i].r1.u, -1}; if(plts[i].t > 1) { ev[++ecnt] = (Event) {plts[i].r2.l, plts[i].r2.d, plts[i].r2.u, 1}; ev[++ecnt] = (Event) {plts[i].r2.r + 1, plts[i].r2.d, plts[i].r2.u, -1}; } } else { ptmp[p4--] = plts[i]; } } sort(ev + 1, ev + ecnt + 1); sort(qs + 1, qs + qcnt + 1); for(register int i = 1; i <= qcnt; ++i) { while(ep <= ecnt && ev[ep].x <= qs[i].x) BIT::add(ev[ep].d, ev[ep].u, ev[ep].t), ++ep; tmp = BIT::query(qs[i].y); if(tmp < frts[qs[i].id].k) frts[qs[i].id].k -= tmp, ftmp[p2--] = frts[qs[i].id]; else ftmp[p1++] = frts[qs[i].id]; } for(register int i = fl; i < fr; ++i) frts[i] = ftmp[i]; for(register int i = pl; i < pr; ++i) plts[i] = ptmp[i]; while(ep <= ecnt) BIT::add(ev[ep].d, ev[ep].u, ev[ep].t), ++ep; solve(pl, p3, fl, p1, L, mid); solve(p3, pr, p1, fr, mid, R); } int main() { scanf("%d%d%d", &n, &N, &M); for(register int i = 1; i < n; ++i) { scanf("%d%d", &u, &v); adde(u, v), adde(v, u); } dfs(1, 0), dfs2(1, 1); int lca; for(register int i = 1; i <= N; ++i) { scanf("%d%d%d", &u, &v, &w); lca = LCA(u, v); if(dfn[u] > dfn[v]) swap(u, v); if(lca == u) { lca = Fdown(u, v); if(dfned[lca] == n) { plts[i] = (Plate) {1, w, (Rect) {1, dfn[lca] - 1, dfn[v], dfned[v]}, 0, 0, 0, 0}; } else { plts[i] = (Plate) {2, w, (Rect) {1, dfn[lca] - 1, dfn[v], dfned[v]}, (Rect) {dfn[v], dfned[v], dfned[lca] + 1, n}}; } } else { plts[i] = (Plate) {1, w, (Rect) {dfn[u], dfned[u], dfn[v], dfned[v]}, 0, 0, 0, 0}; } } for(register int i = 1; i <= M; ++i) { scanf("%d%d%d", &u, &v, &w); if(dfn[u] > dfn[v]) swap(u, v); frts[i] = (Fruit) {dfn[u], dfn[v], w, i}; } sort(plts + 1, plts + N + 1); solve(1, N + 1, 1, M + 1, 0, 1e9 + 1); for(register int i = 1; i <= M; ++i) printf("%d\n", ans[i]); return 0; }
没有帐号? 立即注册