【WC2013】糖果公园 (树上带修改莫队)

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

糖果公园的结构十分奇特,它由 nn 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 11 至 nn。有 n1n−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,则 1xn1≤x≤n1ym1≤y≤m,表示编号为 xx 的游览点发放的糖果类型改为 yy

若 tt 为 11,则 1x,yn1≤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

限制与约定

对于所有的数据,1vi,wi1061≤vi,wi≤1061ai,bin1≤ai,bi≤n1cim1≤ci≤mw1,w2,,wnw1,w2,…,wn 是非递增序列,即对任意 1<in1<i≤n,满足 wiwi1wi≤wi−1

测试点编号 nn mm qq 其它限制
1 20≤20 20≤20 20≤20
2 2000≤2000 2000≤2000 2000≤2000
3 10000≤10000 10000≤10000 10000≤10000
4 80000≤80000 100≤100 80000≤80000 没有修改操作;给出的图构成一条链
5 90000≤90000 100≤100 90000≤90000
6 80000≤80000 80000≤80000 80000≤80000 没有修改操作
7 90000≤90000 90000≤90000 90000≤90000
8 80000≤80000 80000≤80000 80000≤80000 给出的图构成一条链
9 90000≤90000 90000≤90000 90000≤90000
10 100000≤100000 100000≤100000 100000≤100000

祝大家一遍 AC,求不虐萌萌哒测评机!

时间限制8s8s

空间限制512MB512MB




解题思路:用来学习莫队,挺好懂,希望以后做题能用上~!!


#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int MAXN = 100010;
ll W[MAXN], V[MAXN];
int N, M, Q;
int C[MAXN];

struct edge
{
	int to;
	int next;
} e[MAXN << 1];
int edge_num, head[MAXN];
void insert_edge(int u, int v)
{
	e[edge_num].to = v;
	e[edge_num].next = head[u];
	head[u] = edge_num++;
}

int blocksize, blocknum;
int sta[MAXN];
int top;
int deep[MAXN];
int block[MAXN];
int fa[MAXN][25], bin[25]; //求公共祖先用
void dfs(int x)
{
	int bottom = top;
	for (int i = 1; i < 25; i++)
		if (deep[x] >= bin[i])
			fa[x][i] = fa[fa[x][i - 1]][i - 1];
		else
			break;
	for (int i = head[x]; ~i; i = e[i].next)
	{
		int to = e[i].to;
		if (to != fa[x][0])
		{
			fa[to][0] = x;
			deep[to] = deep[x] + 1;
			dfs(to);
			if (top - bottom >= blocksize)
			{
				blocknum++;
				while (top != bottom)
					block[sta[top--]] = blocknum;
			}
		}
		sta[++top] = x;
	}
}

int LCA(int x, int y)
{
	if (deep[x] < deep[y])
		swap(x, y);
	int t = deep[x] - deep[y];
	for (int i = 0; bin[i] <= t; i++)
		if (t & bin[i])
			x = fa[x][i];
	for (int i = 24; i >= 0; i--)
		if (fa[x][i] != fa[y][i])
			x = fa[x][i], y = fa[y][i];
	if (x == y)
		return x;
	return fa[x][0];
}

struct Query
{
	int l;
	int r;
	int cti;
	int id;
} q[MAXN];
int qn;
bool cmp(Query a, Query b)
{
	if (block[a.l] != block[b.l])
		return block[a.l] < block[b.l];
	if (block[a.r] != block[b.r])
		return block[a.r] < block[b.r];
	return a.cti < b.cti;
}
struct Change
{
	int x;
	int val;
} cha[MAXN];
int cn;

ll res = 0;
ll ans[MAXN];

int num[MAXN], now;
bool vis[MAXN];//记录当前是否访问过

void Work(int x)//单点修改,重新暴力计算答案
{
	if (vis[x])//如果访问了,那就变成没访问
	{
		res -= V[C[x]] * W[num[C[x]]];
		num[C[x]]--;
		vis[x] = 0;
	}
	else
	{
		num[C[x]]++;
		res += V[C[x]] * W[num[C[x]]];
		vis[x] = 1;
	}
}

void Move(int x, int y)//暴力移动,不断地往最近公共祖先靠
{
	while (x != y)
	{
		if (deep[x] > deep[y])
			swap(x, y);
		Work(y);
		y = fa[y][0];
	}
}

void Change(int x, int &val)//时间漫游时做修改
{
	if (vis[x])
	{
		Work(x);//先变成没修改
		swap(C[x],val);//交换,用于下次漫游
		Work(x);//再修改为当前值的答案
	}
	else//即使没被访问,也要修改,为了后面漫游一致性
		swap(C[x],val);
}

void Traval(int ti)//时间漫游,把修改过的改回去,没修改的暴力修改
{
	for (int i = now + 1; i <= ti; i++)
		Change(cha[i].x, cha[i].val);
	for (int i = now; i > ti; i--)
		Change(cha[i].x, cha[i].val);
	now = ti;
}

int main()
{
	bin[0] = 1;
	for (int i = 1; i < 25; i++)
		bin[i] = bin[i - 1] << 1;
	memset(head, -1, sizeof(head));
	edge_num = 0;

	scanf("%d%d%d", &N, &M, &Q);
	blocksize = pow(N, 2.0 / 3.0);
	int x, y;
	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 = 2; i <= N; i++)
	{
		scanf("%d%d", &x, &y);
		insert_edge(x, y);
		insert_edge(y, x);
	}
	dfs(1);
	blocknum++;
	while (top)
		block[sta[top--]] = blocknum;

	for (int i = 1; i <= N; i++)
		scanf("%d", &C[i]);

	int op;
	for (int i = 1; i <= Q; i++)
	{
		scanf("%d%d%d", &op, &x, &y);
		if (op)
		{
			if (block[x] > block[y])//小优化,使得块直接更加接近
				swap(x, y);
			q[qn].l = x;
			q[qn].r = y;
			q[qn].id = qn;
			q[qn].cti = cn;
			qn++;
		}
		else
		{
			cn++;
			cha[cn].x = x;
			cha[cn].val = y;
		}
	}

	sort(q, q + qn, cmp);

	int lca = LCA(q[0].l, q[0].r);
	Traval(q[0].cti);
	Move(q[0].l, q[0].r);
	Work(lca);
	ans[q[0].id] = res;
	Work(lca);
	for (int i = 1; i < qn; i++)
	{
		Traval(q[i].cti);
		Move(q[i - 1].l, q[i].l);
		Move(q[i - 1].r, q[i].r);
		lca = LCA(q[i].l, q[i].r);//lca要单独计算,画画图就知道了
		Work(lca);
		ans[q[i].id] = res;
		Work(lca);
	}
	for (int i = 0; i < qn; i++)
		printf("%lld\n", ans[i]);

	return 0;
}







猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/79967568