BZOJ 3052 带修改的树上莫队

Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。


糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 11 至 nn。有 n−1n−1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。


糖果公园所发放的糖果种类非常丰富,总共 mm 种,它们的编号依次为 11 至 mm。每一个糖果发放处都只发放某种特定的糖果,我们用 cici 来表示 ii 号游览点的糖果。


来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。


大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 ii 种糖果的美味指数为 vivi。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 ii 次品尝某类糖果的新奇指数 wiwi,如果一位游客第 ii 次品尝第 jj 种糖果,那么他的愉悦指数 HH 将会增加对应的美味指数与新奇指数的乘积,即 vjwivjwi。这位游客游览公园的愉悦指数最终将是这些乘积的和。


当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 mm 种中的一种),这样的目的是能够让游客们总是感受到惊喜。


糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。


输入格式
第一行包含三个正整数 n,m,qn,m,q,分别表示游览点个数、糖果种类数和操作次数。


第二行包含 mm 个正整数 v1,v2,…,vmv1,v2,…,vm。


第三行包含 nn 个正整数 w1,w2,…,wnw1,w2,…,wn。


第四行到第 n+2n+2 行,每行包含两个正整数 ai,biai,bi,表示这两个游览点之间有路径可以直接到达。


第 n+3n+3 行包含 nn 个正整数 c1,c2,…,cnc1,c2,…,cn。


接下来 qq 行,每行包含三个整数 t,x,yt,x,y,表示一次操作:


若 tt 为 00,则 1≤x≤n1≤x≤n,1≤y≤m1≤y≤m,表示编号为 xx 的游览点发放的糖果类型改为 yy;


若 tt 为 11,则 1≤x,y≤n1≤x,y≤n,表示对出发点为 xx,终止点为 yy 的路线询问愉悦指数。


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


样例一
input


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


output


84
131
27

84


题目大意:给定一棵树,每个点有一个颜色,提供两种操作:
1.询问两点间路径上的Σv[a[i]]*w[k],其中a[i]代表这个点的颜色,k表示这个点是这种颜色第k次出现
2.修改某个点的颜色

带修改的莫队:将询问按照 左端点所在的块编号为第一关键字,右端点所在的块为第二关键字,位于第几次修改之后为第三关键字 排序。

该题参考http://blog.csdn.net/qzh_1430586275/article/details/51434506

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100100;
typedef long long ll;
inline int read()
{
	char ch = getchar();
	int x = 0, f = 1;
	while (ch < '0' || ch > '9')
	{
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while ('0' <= ch && ch <= '9')
	{
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
struct Edge
{
	int  en, next;
} E[maxn * 2];
int head[maxn], num;
int first[maxn];
int n, m, q;
int v[maxn], w[maxn], col[maxn], cnt[maxn];
int tid[maxn], st[maxn], stn, dep[maxn], fa[maxn][20], BLOCKSIZE, blockclk;
bool in[maxn];
ll ansnow, ans[maxn];
struct node
{
	int x, y, z;
} Q[maxn];
struct Node
{
	int x, y, z, id;
	ll ans;
	inline void sw()
	{
		if (tid[x] > tid[y]) swap(x, y);
	}
	inline bool operator < (const Node& b) const
	{
		if (tid[x] != tid[b.x]) return tid[x] < tid[b.x];
		if (tid[y] != tid[b.y]) return tid[y] < tid[b.y];
		return z < b.z;
	}
} query[maxn];
inline void add(int st, int en)
{
	E[num].en = en;
	E[num].next = head[st];
	head[st] = num++;
}
inline void update(int x)
{
	if (in[x]) ansnow -= (ll)w[cnt[col[x]] --] * v[col[x]];
	else ansnow += (ll)w[++cnt[col[x]]] * v[col[x]];
	in[x] ^= 1;
}
inline void modify(int x, int y)
{
	if (in[x])
	{
		update(x);
		col[x] = y;
		update(x);
	}
	else col[x] = y;
}
inline void moveto(int x, int y)
{
	while (x != y)
	{
		if (dep[x] < dep[y]) swap(x, y);
		update(x);
		x = fa[x][0];
	}
}
inline void dfs(int x, int p, int h)
{
	int bt = stn;
	dep[x] = h;
	fa[x][0] = p;
	for (int i = head[x]; i != -1; i = E[i].next)
	{
		if (E[i].en == p) continue;
		dfs(E[i].en, x, h + 1);
		if (stn - bt >= BLOCKSIZE)
		{
			blockclk++;
			while (stn != bt)
				tid[st[stn--]] = blockclk;
		}
	}
	st[++stn] = x;
}
inline void init()
{
	for (int i = 1; i <= 18; i++)
		for (int j = 1; j <= n; j++)
			fa[j][i] = fa[fa[j][i - 1]][i - 1];
}
inline int LCA(int x, int y)
{
	if (dep[x] < dep[y]) swap(x, y);
	int t = dep[x] - dep[y];
	for (int i = 18; i >= 0; i--)
		if (t & (1 << i))
			x = fa[x][i];
	if (x == y) return x;
	for (int i = 18; i >= 0; i--)
	{
		if (fa[x][i] != fa[y][i])
		{
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];
}
inline void getans(int x, int y)
{
	update(y);
	ans[query[x].z] = ansnow;
	update(y);
}
int main()
{
	n = read();
	m = read();
	q = read();
	memset(head, -1, sizeof(head));
	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++)
	{
		int a = read(), b = read();
		add(a, b), add(b, a);
	}
	BLOCKSIZE = pow(n, 2.0 / 3.0);
	for (int i = 1; i <= n; i++)
		col[i] = read(), first[i] = col[i];
	dfs(1, 0, 0);
	while (stn)
		tid[st[stn--]] = blockclk;
	init();
	int cnt1 = 0, cnt2 = 0;
	for (int i = 1; i <= q; i++)
	{
		int t = read();
		if (t == 0)
		{
			Q[++cnt1].x = read();
			Q[cnt1].y = first[Q[cnt1].x];
			Q[cnt1].z = first[Q[cnt1].x] = read();
		}
		else
		{
			query[++cnt2].x = read();
			query[cnt2].y = read();
			query[cnt2].z = cnt2;
			query[cnt2].id = cnt1;
			query[cnt2].sw();
		}
	}
	sort(query + 1, query + cnt2 + 1);
	int tim = 1;
	while (tim <= query[1].id)
	{
		modify(Q[tim].x, Q[tim].z);
		tim++;
	}
	moveto(query[1].x, query[1].y);
	getans(1, LCA(query[1].x, query[1].y));
	for (int i = 2; i <= cnt2; i++)
	{
		while (tim <= query[i].id) modify(Q[tim].x, Q[tim].z), tim++;
		while (tim > query[i].id + 1) modify(Q[tim - 1].x, Q[tim - 1].y), tim--;
		moveto(query[i - 1].x, query[i].x);
		moveto(query[i - 1].y, query[i].y);
		getans(i, LCA(query[i].x, query[i].y));
	}
	for (int i = 1; i <= cnt2; i++)
		printf("%lld\n", ans[i]);
	system("pause");
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值