列表

详情


NC200517. 糖果公园

描述

有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩。
糖果公园的结构十分奇特,它由 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 。有 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。
糖果公园所发放的糖果种类非常丰富,总共有 种,它们的编号依次为 。每一个糖果发放处都只发放某种特定的糖果,我们用 C_i 来表示 号游览点的糖果。
来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。
大家对不同类型糖果的喜爱程度都不尽相同。 根据游客们的反馈打分,我们得到了糖果的美味指数, 第 种糖果的美味指数为 V_i 。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 次品尝某类糖果的新奇指数 W_i 。如果一位游客第 次品尝第 种糖果,那么他的愉悦指数 将会增加对应的美味指数与新奇指数的乘积,即 。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。

输入描述

第一行包含三个正整数 ,, , 分别表示游览点个数、 糖果种类数和操作次数。
第二行包含 个正整数
第三行包含 个正整数
第四行到第 行,每行包含两个正整数 A_i, B_i,表示这两个游览点之间有路径可以直接到达。
行包含 个正整数
接下来 行, 每行包含三个整数 ,表示一次操作:
,则 ,表示将编号为 的游览点发放的糖果类型改为
,则 ,表示对出发点为 ,终止点为 的路线询问愉悦指数。

输出描述

按照输入的先后顺序,对于每个   的操作输出一行,用一个正整数表示答案。

示例1

输入:

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

说明:

我们分别用

代表 C_i 为 1、2、3 的节点,在修改之前:

在将 C_2 修改为 1 之后:

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++11) 解法, 执行用时: 1696ms, 内存消耗: 20100K, 提交时间: 2020-12-13 23:35:57

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int NN = 100010, LN = 20;
struct Edge {int v, next;} ES[NN * 2];
int Next[NN], EC = 0;
void add_edge(int x, int y) {
  ES[++EC] = {y, Next[x]};
  Next[x] = EC;
}
stack<int> Stk;
int Dep[NN], fa[NN][LN], 							//LCA
    BlkId[NN], BLOCK, BlkCnt = 0, 					//Block
    C[NN], CNT[NN], VIS[NN], MC = 0, QC = 0, CBak[NN]; //莫队
LL V[NN], W[NN], CurAns = 0, Ans[NN];
void dfs(int x) { 									//预处理LCA以及树的分块
  Dep[x] = Dep[fa[x][0]] + 1;
  for (int i = 1; (1 << i) <= Dep[x]; i++)
    fa[x][i] = fa[fa[x][i - 1]][i - 1];
  size_t now = Stk.size();
  for (int i = Next[x]; i; i = ES[i].next) {
    int v = ES[i].v;
    if (v == fa[x][0]) continue;
    fa[v][0] = x;
    dfs(v);
    if (Stk.size() - now > BLOCK) { 				//树分块
      BlkCnt++;
      while (Stk.size() > now) BlkId[Stk.top()] = BlkCnt, Stk.pop();
    }
  }
  Stk.push(x);
  if (x == 1) {
    BlkCnt++;
    while (!Stk.empty()) BlkId[Stk.top()] = BlkCnt, Stk.pop();
  }
}

int LCA(int x, int y) { 					//倍增LCA
  if (Dep[x] < Dep[y]) swap(x, y);
  for (int i = LN; i >= 0; i--)
    if ((1 << i) <= Dep[x] - Dep[y]) x = fa[x][i];
  if (x == y) return x;
  for (int i = LN; i >= 0; i--)
    if (Dep[x] >= (1 << i) && fa[x][i] != fa[y][i])
      x = fa[x][i], y = fa[y][i];
  return fa[x][0];
}

struct Modify {int pos, color, old_color;} MS[NN];
struct Query {
  int x, y, time, id;
  bool operator < (const Query &b) const {
    if (BlkId[x] != BlkId[b.x]) return BlkId[x] < BlkId[b.x];
    if (BlkId[y] != BlkId[b.y]) return BlkId[y] < BlkId[b.y];
    return time < b.time;
  }
} QS[NN];

inline void add_pos(int u) { CurAns+=W[++CNT[C[u]]]*V[C[u]]; } //莫队
inline void del_pos(int u) { CurAns -= W[CNT[C[u]]--]*V[C[u]]; }
inline void modify(int u, int c) {			// 改变u的颜色
  if (VIS[u]) del_pos(u), C[u] = c, add_pos(u);
  else C[u] = c;
}
inline void invert(int u) {					//切换u的选中状态
  if (VIS[u]) VIS[u] = 0, del_pos(u);
  else VIS[u] = 1, add_pos(u);
}
inline void mark(int u, int l) {			//mark [u->l), l是u的祖先
  for (int x = u; x != l; x = fa[x][0]) invert(x);
}

int main() {
  int N, M, Q;
  scanf("%d%d%d", &N, &M, &Q);
  BLOCK = (int)(pow(N, 2.0 / 3) / 2);
  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, u, v; i < N; i++)
    scanf("%d%d", &u, &v), add_edge(u, v), add_edge(v, u);
  for (int i = 1; i <= N; i++) scanf("%d", &C[i]), CBak[i] = C[i];

  dfs(1);
  for (int i = 1, op; i <= Q; i++) {
    scanf("%d", &op);
    if (op == 0) {
      Modify& m = MS[++MC];
      scanf("%d%d", &m.pos, &m.color), m.old_color = C[m.pos], C[m.pos] = m.color;
    }
    else {
      Query& q = QS[++QC];
      scanf("%d%d", &q.x, &q.y), q.time = MC, q.id = QC;
      if (BlkId[q.x] > BlkId[q.y]) swap(q.x, q.y); //y的移动就会少一半
    }
  }
  sort(QS + 1, QS + 1 + QC);
  for (int i = 1; i <= N; i++) C[i] = CBak[i];

  int X = 1, Y = 1; QS[0].time = 0;				//[X,Y]:包含当前结果的区间
  for (int i=1; i<=QC; i++) {	//维护X,Y路径上不包含 lca(X,Y) 的点的贡献和
    const Query& q = QS[i];
    int l = LCA(q.x, X), l2 = LCA(q.y, Y), last = QS[i - 1].time;
    mark(X, l), mark(q.x, l), mark(Y, l2), mark(q.y, l2); //X→q.x, Y→q.y
    X = q.x, Y = q.y, l = LCA(X, Y);
    invert(l);
    for (int t = last + 1; t <= q.time; t++) modify(MS[t].pos, MS[t].color);
    for (int t = last; t > q.time; t--) modify(MS[t].pos, MS[t].old_color);
    Ans[q.id] = CurAns;
    invert(l);
  }
  for (int i = 1; i <= QC; i++) printf("%lld\n", Ans[i]);
}

上一题