【BZOJ3052】【UOJ58】【WC2013】糖果公园

【题目链接】

【思路要点】

  • 维护一个糖果集合,支持加入一个糖果,删除一个糖果,显然,我们可以容易地在\(O(1)\)的时间内实现这些操作。
  • 考虑使用莫队算法。对树分块,运行树上带修莫队即可。
  • 实际上,这是一道树上带修莫队的模板题,相当于将带修莫队、树上莫队和树分块合为一体。
  • 时间复杂度\(O(N^{\frac{5}{3}})\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	100005
#define SIZE	2100
#define MAXLOG	20
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
struct querys {int x, y, lca, timer, home; } b[MAXN];
struct changes {int pos, val; } c[MAXN];
vector <int> a[MAXN];
int father[MAXN][MAXLOG], depth[MAXN];
int num[MAXN], tot;
int stk[MAXN], top;
int n, m, q, qb, qc, nowc;
long long ans[MAXN], nowans;
long long vm[MAXN], vn[MAXN];
int col[MAXN], now[MAXN], cnt[MAXN];
bool exist[MAXN];
int lca(int x, int y) {
	if (depth[x] < depth[y]) swap(x, y);
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (depth[father[x][i]] >= depth[y]) x = father[x][i];
	if (x == y) return x;
	for (int i = MAXLOG - 1; i >= 0; i--)
		if (father[x][i] != father[y][i]) {
			x = father[x][i];
			y = father[y][i];
		}
	return father[x][0];
}
void add(int type) {
	cnt[type]++;
	nowans += vn[cnt[type]] * vm[type];
}
void dec(int type) {
	nowans -= vn[cnt[type]] * vm[type];
	cnt[type]--;
}
void Xor(int x, int fa) {
	while (x != fa) {
		if (exist[x]) {
			exist[x] = false;
			dec(now[x]);
		} else {
			exist[x] = true;
			add(now[x]);
		}
		x = father[x][0];
	}
}
void move(int x, int y) {
	int Lca = lca(x, y);
	Xor(x, Lca), Xor(y, Lca);
}
void modify(int x, int y) {
	if (!exist[x]) {
		now[x] = y;
		return;
	}
	dec(now[x]);
	now[x] = y;
	add(now[x]);
}
void process(int l, int r) {
	for (int i = 1; i <= n; i++)
		now[i] = col[i];
	memset(cnt, 0, sizeof(cnt));
	memset(exist, false, sizeof(exist));
	nowans = nowc = 0;
	while (nowc < b[l].timer) {
		nowc++;
		modify(c[nowc].pos, c[nowc].val);
	}
	move(b[l].x, b[l].y);
	add(now[b[l].lca]);
	ans[b[l].home] = nowans;
	dec(now[b[l].lca]);
	for (int i = l + 1; i <= r; i++) {
		while (nowc < b[i].timer) {
			nowc++;
			modify(c[nowc].pos, c[nowc].val);
		}
		move(b[i].x, b[i - 1].x);
		move(b[i].y, b[i - 1].y);
		add(now[b[i].lca]);
		ans[b[i].home] = nowans;
		dec(now[b[i].lca]);
	}
}
void work(int pos, int fa, int start) {
	father[pos][0] = fa;
	depth[pos] = depth[fa] + 1;
	for (int i = 1; i < MAXLOG; i++)
		father[pos][i] = father[father[pos][i - 1]][i - 1];
	for (unsigned i = 0; i < a[pos].size(); i++) {
		if (a[pos][i] == fa) continue;
		work(a[pos][i], pos, top);
		if (top - start >= SIZE) {
			tot++;
			for (int i = start + 1; i <= top; i++)
				num[stk[i]] = tot;
			top = start;
		}
	}
	stk[++top] = pos;
	if (top - start >= SIZE) {
		tot++;
		for (int i = start + 1; i <= top; i++)
			num[stk[i]] = tot;
		top = start;
	}
}
bool cmp(querys a, querys b) {
	if (num[a.x] == num[b.x]) {
		if (num[a.y] == num[b.y]) return a.timer < b.timer;
		else return num[a.y] < num[b.y];
	} else return num[a.x] < num[b.x];
}
int main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= m; i++)
		read(vm[i]);
	for (int i = 1; i <= n; i++)
		read(vn[i]);
	for (int i = 1; i <= n - 1; i++) {
		int x, y;
		read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	for (int i = 1; i <= n; i++)
		read(col[i]);
	work(1, 0, 0);
	if (top) {
		tot++;
		for (int i = 1; i <= top; i++)
			num[stk[i]] = tot;
		top = 0;
	}
	for (int i = 1; i <= q; i++) {
		int opt, x, y;
		read(opt), read(x), read(y);
		if (opt == 0) c[++qc] = (changes) {x, y};
		else {
			if (num[x] > num[y]) swap(x, y);
			b[++qb] = (querys) {x, y, lca(x, y), qc, qb};
		}
	}
	sort(b + 1, b + qb + 1, cmp);
	int last = 0;
	for (int i = 1; i <= qb; i++)
		if (num[b[i].x] != num[b[i + 1].x] || num[b[i].y] != num[b[i + 1].y]) {
			process(last + 1, i);
			last = i;
		}
	for (int i = 1; i <= qb; i++)
		printf("%lld\n", ans[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值