bzoj 3052. [wc2013]糖果公园 树上带修莫队
题目
bzoj3052 [wc2013]糖果公园 (https://darkbzoj.tk/problem/3052)
题目大意
有一座糖果公园,有n个游览点构成(编号1~n),每个游览点都有一个糖果发放处。有n-1条双向道路,形成一个树结构。
糖果公园所发放的糖果数量非常丰富,总共有m种,题目的编号为1~m。每个发放点只发放一个特定的糖果。
来到公园的人,总是从一个游览点出发点通过最短路径到达另一游览点。他们每经过一个游览点就可以得到相应一个糖果。
大家对不同类型的糖果的喜欢程度不同,如果游客第i次品尝第j种糖果,那么他的愉悦指数。
当然公园糖果发放点的发放糖果的种类不一定是一成不变的。有时,一些糖果点发放的糖果种类可能会改变(也会是m种中的一种)
这样的目的是能够让游客们感到惊喜。
现在你的任务是统计每个游客的愉悦指数。
输入
n, m ,q
v[1], v[2], ... , v[m]
w[1], w[2], ... , w[n]
A[i] B[i](A[i]和B[i]有边)
....
A[n-1] B[n-1]
c[1], c[2], ... , c[n] (每个游览点的发放糖果种类)
接下来q行 Type x y
Type=0, 把x游览点的糖果种类换成y
Type=1,查询从x出发到y的愉悦指数
输出
对于每次查询输出H
数据范围
1<=n, m, q<=1e5, time=100s
思路
树上带修莫队的模板题。时间复杂度N^(5/3)
ps:树链剖分LCA更快
//18324ms
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct edge {
int to, nxt;
} e[200005<<1];
int head[200005], lg[200005], cut=0;
struct Tree_lca {
int f[200005][22], deep[200005], in[200005], out[200005], T=0;
int id[200005];
void init() {
memset(head, -1, sizeof(head));
cut=T=0;
}
void addedge(int x, int y) {
e[++cut]= {y, head[x]};
head[x]=cut;
}
void dfs(int u, int fa) {
f[u][0]=fa;
in[u]=++T;
id[T]=u;
deep[u]=deep[fa]+1;
for(int i=1; i<=lg[deep[u]]; i++) {
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=head[u]; i!=-1; i=e[i].nxt) {
int to=e[i].to;
if(to!=fa) {
dfs(to, u);
}
}
out[u]=++T;
id[T]=u;
}
int LCA(int x, int y) {
if(deep[x]<deep[y])
swap(x, y);
int d=deep[x]-deep[y];
for(int i=0; d; i++) {
if(d&(1<<i)) {
x=f[x][i];
d-=(1<<i);
}
}
if(x==y)
return x;
for(int i=lg[deep[y]]; i>=0; i--) {
if(f[x][i]!=f[y][i]) {
x=f[x][i], y=f[y][i];
}
}
return f[x][0];
}
} Tree;
int vis[200005], c[200005];
LL v[200005], w[200005];
LL sum[200005], ans[200005];
LL ANS=0;
void add(int x) {
sum[x]++;
ANS+=(w[sum[x]]*v[x]);
}
void del(int x) {
ANS-=(w[sum[x]]*v[x]);
sum[x]--;
}
void calc(int x) {
x=Tree.id[x];
if(!vis[x]) {
add(c[x]);
} else {
del(c[x]);
}
vis[x]^=1;
}
struct Qry {
int op, l, r, t, k, lca, i;
} q[200005], qx[200005];
void up(int i, int t) {//时间轴的移动
if(vis[qx[t].l]) {//如果修改的点在路径上,那么修改造成贡献
del(c[qx[t].l]);
add(qx[t].r);
}
swap(c[qx[t].l], qx[t].r);
}
int len;
void getans(int Q) {
sort(q+1, q+Q+1, [](const Qry &a, const Qry &b){
if (a.l/len!=b.l/len) return a.l<b.l;
if (a.r/len!=b.r/len) return a.r<b.r;
return a.t<b.t;
});
int L=1, R=0, Ti=0;
for(int i=1; i<=Q; i++) {
while(L<q[i].l) {
calc(L);
L++;
}
while(L>q[i].l) {
L--;
calc(L);
}
while(R<q[i].r) {
R++;
calc(R);
}
while(R>q[i].r) {
calc(R);
R--;
}
if(q[i].lca)
calc(q[i].lca);
//时间轴的影响
while(Ti<q[i].t) {
up(i, ++Ti);
}
while(Ti>q[i].t) {
up(i, Ti--);
}
ans[q[i].i]=ANS;
if(q[i].lca)
calc(q[i].lca);
}
}
int main() {
lg[1]=1;
for(int i=2; i<=200005; i++) {
lg[i]=lg[i>>1]+1;
}
int n, m, Q;
scanf("%d%d%d", &n, &m, &Q);
Tree.init();
len=sqrt(2*n);
for(int i=1; i<=m; i++) {
scanf("%lld", &v[i]);
}
for(int i=1; i<=n; i++) {
scanf("%lld", &w[i]);
}
for(int i=1; i<=n-1; i++) {
int x, y;
scanf("%d%d", &x, &y);
Tree.addedge(x, y);
Tree.addedge(y, x);
}
for(int i=1; i<=n; i++) {
scanf("%d", &c[i]);
}
Tree.dfs(1, 0);
int cut_x=0, cut_q=0;
for(int i=1; i<=Q; i++) {
int op, x, y;
scanf("%d%d%d",&op, &x, &y);
int lca=Tree.LCA(x, y);
if(op==1) {//查询
if(Tree.in[x]>Tree.in[y]) {
swap(x, y);
}
++cut_q;
q[cut_q].l=(lca==x)?Tree.in[x]:Tree.out[x];
q[cut_q].r=Tree.in[y];
q[cut_q].t=cut_x;//记录查询是第cut_x次修改后面
q[cut_q].k=q[i].l/len;
q[cut_q].lca=(lca==x)?0:Tree.in[lca];
q[cut_q].i=cut_q;
} else {//修改
++cut_x;
qx[cut_x].l=x;
qx[cut_x].r=y;
qx[cut_x].lca=(lca==x)?0:Tree.in[lca];
}
}
getans(cut_q);
for(int i=1; i<=cut_q; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}
/*
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2
84
131
27
84
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)