【luogu P4074】【ybt金牌导航6-4-3】糖果公园(带修莫队)(树上莫队)

糖果公园

题目链接:luogu P4074 / ybt金牌导航6-4-3

题目大意

给你一棵树,树上点有颜色,颜色有权值,然后还给出新鲜度。
然后有一些操作,可能是修改一个点的颜色,可能是给你一条路径,问你路径的分数。
路径的分数是按顺序经过点,每个点给分数的贡献是它颜色的权值乘上新鲜度的第 i 项(i 是你第几次经到这个颜色的点)。
询问之间独立。

思路

这道题其实就是模板的带修树上莫队题。

首先就是如何分块,我们考虑用这样的一种分块方式:
我们每遍历处理完一个点的子树,就把这个点放入队列中。然后如果当前这个点的儿子要处理的点数超过了大小,就把那些找出来标成一类。

然后接着有时间就是 n23 块长,然后就是正常的搞搞待修莫队。
然后你会发现询问的修改好像不知道要怎么移动?

然后其实你通过观察会发现,我们莫队不是有一个点是否贡献吗,你会发现你原来是 (x1,y1),现在是 (x2,y2),我们其实就把 x1x2y1y2 这两个路径上的点状态取反即可。
由于我们是莫队,那我们就可以暴力的移动修改。

然后 ybt 要卡常,我卡了三页真的卡不动了。

代码(这个ybt会只有70)

//#pragma GCC optimize(2) #include<cmath> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int N = 100005; struct ask { int t, x, y, num; }a[N]; int pl[N], bef[N], to[N], num[N]; int n, m, q, v[N], w[N], bn; int tim, qn, op, x, y, t; int c[N], lst[N], sz, bl[N], LCA; int deg[N], fa[N][18], sta[N]; bool in[N]; ll ans, an[N]; struct node { int to, nxt; }e[N << 1]; int le[N], KK; inline void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } inline int read() { int re = 0; char c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re; } inline int dfs_bl(int now, int father) {//分块 fa[now][0] = father; deg[now] = deg[father] + 1; int sn = 0; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to ^ father) { sn += dfs_bl(e[i].to, now); if (sn >= sz) { bn++; while (sn) { bl[sta[sta[0]]] = bn; sta[0]--; sn--; } } } sta[++sta[0]] = now; sn++; return sn; } bool cmp(ask x, ask y) {//莫队排序 if (bl[x.x] ^ bl[y.x]) return bl[x.x] < bl[y.x]; if (bl[x.y] ^ bl[y.y]) return (bl[x.x] & 1) ? bl[x.y] < bl[y.y] : bl[x.y] > bl[y.y]; return ((bl[x.x] ^ bl[x.y]) & 1) ? x.t < y.t : x.t > y.t; } inline int lca(int x, int y) { if (deg[y] > deg[x]) swap(x, y); for (int i = 17; i >= 0; i--) if (deg[fa[x][i]] >= deg[y]) x = fa[x][i]; if (x == y) return x; for (int i = 17; i >= 0; i--) if (fa[x][i] ^ fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } inline void putans(int x) {//更新答案(一个点) if (!in[x]) { num[c[x]]++; ans += 1ll * v[c[x]] * w[num[c[x]]]; } else { ans -= 1ll * v[c[x]] * w[num[c[x]]]; num[c[x]]--; } in[x] ^= 1; } void change(int x, int y) {//给树上点更颜色 if (!in[x]) c[x] = y; else { putans(x); c[x] = y; putans(x); } } inline void turn(int x, int y) {//给树上路径上的所有点统计(LCA不会统计) while (x ^ y) { if (deg[x] > deg[y]) putans(x), x = fa[x][0]; else putans(y), y = fa[y][0]; } } inline void write(ll x) { if (x > 9ll) write(x / 10); putchar(x % 10 + '0'); } int main() { n = read(); m = read(); q = read(); for (int i = 1; i <= m; i++) v[i] = read(); for (int i = 1; i <= n; i++) w[i] = read(); for (int i = 1; i < n; i++) { x = read(); y = read(); add(x, y); } for (int i = 1; i <= n; i++) c[i] = read(), lst[i] = c[i]; for (int i = 1; i <= q; i++) { op = read(); x = read(); y = read(); if (op == 0) { tim++; pl[tim] = x; bef[tim] = lst[x]; to[tim] = y; lst[x] = y; } else { a[++qn] = (ask){tim, x, y, qn}; } } sz = pow(n, 2.0 / 3); int tmp = dfs_bl(1, 0); if (tmp) bn++; while (tmp) { bl[sta[sta[0]]] = bn; sta[0]--; tmp--; } for (int i = 1; i <= 17; i++) for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1]; sort(a + 1, a + qn + 1, cmp); t = a[1].t; for (int i = 1; i <= t; i++) change(pl[i], to[i]); turn(a[1].x, a[1].y); LCA = lca(a[1].x, a[1].y); putans(LCA); an[a[1].num] = ans; putans(LCA); for (int i = 2; i <= qn; i++) {//莫队 while (t < a[i].t) t++, change(pl[t], to[t]); while (t > a[i].t) change(pl[t], bef[t]), t--; turn(a[i].x, a[i - 1].x); turn(a[i].y, a[i - 1].y); LCA = lca(a[i].x, a[i].y); putans(LCA);//前面没有处理到 LCA 所以要单独处理 an[a[i].num] = ans; putans(LCA); } for (int i = 1; i <= qn; i++) write(an[i]), putchar('\n'); return 0; }

后记

使用了新的树上莫队,用了欧拉序然后就过了。

新代码

#include<cmath> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int N = 100001; struct ask { int t, x, y, num, lca; }a[N]; int pl[N], bef[N], to[N], num[N]; int n, m, q, v[N], w[N], bn, tmpn; int tim, qn, op, x, y, t, dfn[2 * N]; int c[N], lst[N], sz, bl[2 * N], LCA; int deg[N], fa[N][18], st[N], ed[N]; bool in[N]; ll ans, an[N]; struct node { int to, nxt; }e[2 * N]; int le[N], KK; inline void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } int re; char cc; inline int read() { re = 0; cc = getchar(); while (cc < '0' || cc > '9') cc = getchar(); while (cc >= '0' && cc <= '9') { re = (re << 3) + (re << 1) + cc - '0'; cc = getchar(); } return re; } inline void dfs_bl(int now, int father) { dfn[++tmpn] = now; st[now] = tmpn; fa[now][0] = father; deg[now] = deg[father] + 1; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to ^ father) { dfs_bl(e[i].to, now); } dfn[++tmpn] = now; ed[now] = tmpn; } inline bool cmp(ask x, ask y) { // if (bl[x.x] ^ bl[y.x]) return bl[x.x] < bl[y.x]; // if (bl[x.y] ^ bl[y.y]) return (bl[x.x] & 1) ? bl[x.y] < bl[y.y] : bl[x.y] > bl[y.y]; // return ((bl[x.x] ^ bl[x.y]) & 1) ? x.t < y.t : x.t > y.t; if (bl[x.x] ^ bl[y.x]) return bl[x.x] < bl[y.x]; if (bl[x.y] ^ bl[y.y]) return bl[x.y] < bl[y.y]; return x.t < y.t; } inline int lca(int x, int y) { if (deg[y] > deg[x]) swap(x, y); for (int i = 17; i >= 0; i--) if (deg[fa[x][i]] >= deg[y]) x = fa[x][i]; if (x == y) return x; for (int i = 17; i >= 0; i--) if (fa[x][i] ^ fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } inline void putans(int x) { if (!in[x]) { num[c[x]]++; ans += 1ll * v[c[x]] * w[num[c[x]]]; } else { ans -= 1ll * v[c[x]] * w[num[c[x]]]; num[c[x]]--; } in[x] ^= 1; } inline void change(int x, int y) { if (!in[x]) c[x] = y; else { putans(x); c[x] = y; putans(x); } } void write(ll x) { if (x > 9ll) write(x / 10); putchar(x % 10 + '0'); } int main() { n = read(); m = read(); q = read(); for (int i = 1; i <= m; i++) v[i] = read(); for (int i = 1; i <= n; i++) w[i] = read(); for (int i = 1; i < n; i++) { x = read(); y = read(); add(x, y); } for (int i = 1; i <= n; i++) c[i] = read(), lst[i] = c[i]; sz = pow(2 * n, 2.0 / 3); dfs_bl(1, 0); for (int i = 1; i <= 17; i++) for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1]; for (int i = 1; i <= 2 * n; i++) bl[i] = (i - 1) / sz + 1; for (int i = 1; i <= q; i++) { op = read(); x = read(); y = read(); if (op == 0) { tim++; pl[tim] = x; bef[tim] = lst[x]; to[tim] = y; lst[x] = y; } else { LCA = lca(x, y); if (st[x] > st[y]) swap(x, y); if (LCA == x) a[++qn] = (ask){tim, st[x], st[y], qn, 0}; else a[++qn] = (ask){tim, ed[x], st[y], qn, LCA}; } } sort(a + 1, a + qn + 1, cmp); x = 1; y = 0; t = 0; for (int i = 1; i <= qn; i++) { while (t < a[i].t) t++, change(pl[t], to[t]); while (t > a[i].t) change(pl[t], bef[t]), t--; while (x < a[i].x) putans(dfn[x]), x++; while (x > a[i].x) x--, putans(dfn[x]); while (y < a[i].y) y++, putans(dfn[y]); while (y > a[i].y) putans(dfn[y]), y--; if (a[i].lca) putans(a[i].lca); an[a[i].num] = ans; if (a[i].lca) putans(a[i].lca); } for (int i = 1; i <= qn; i++) write(an[i]), putchar('\n'); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-4-3.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(24)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示