NOIP模拟 开荒

版权声明: https://blog.csdn.net/Ghost_Pig/article/details/83188419
  • Description

题目背景: 尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门。他加入师门后发现有无穷无尽的师兄弟姐妹,这几天新副本开了,尊者神高达的师门作为一个 pve师门,于是他们决定组织一起去开荒。

题目描述: 师门可以看做以 1 为根的一棵树,师门中的每一个人都有一定的装备分数。一共会有 q 个事件。每个事件可能是一次开荒,也可能是因为开荒出了好装备而导致一个人的装分出现了变化。对于一次开荒,会有 k 个人组织,由于师门的号召力很强,所以所有在组织者中任意两个人简单路径上的人都会参加。

  • Input

第一行 n ,q; 接下来 1 行 n 个数,代表每个人的分值; 接下来 n-1 行 u,v 代表一条边 接下来 q 行 Q 代表询问,接下来 k 个数代表组织的人数,读入为 0时停止读入。 C 代表修改,输入 x,w 代表将 x 的分值变为 w

  • Output

共 Q 的数量行,为开荒的人的总分值

  • Sample Input

4 4

10 5 2 2

1 2

2 3

2 4

Q 3 4 0

C 3 200

Q 3 4 0

Q 1 4 0

  • Sample Output

9

207

17

样例解释:

第一次询问,参加的人有 2,3,4 5+2+2=9

第一次修改,权值为 10 5 200 2

第二次询问,参加的人有 2,3,4 5+200+2=207

第三次询问,参加的人有 1,2,4 10+5+2=17

  • Data Constraint

数据范围:

20%的数据 n<=10000,q<=500;

另外 20%的数据 k=2

另外 20%的数据 没有修改操作

所有数据 n,q<=100000,所有询问 k 的和<=1000000

保证数据合法

  • 题目分析
    这道题我们首先发现其实我们求的是树上两点间路径的每个点的价值和,但会有多个点
    这里我们每次将相邻两点的的路径上的和加入ans
    但每条路径会被统计两次,我们就将ans/2
    但是lca会用到不止两次,所以我们将lca单独提出来运算
    用树剖模板来一发即可

下面是代码:

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll Maxn = 1e5 + 5;
ll  n,m,b[Maxn],
	last[Maxn],size[Maxn],
	v[Maxn],deep[Maxn],
	top[Maxn],fa[Maxn],
	f[Maxn * 4],tr1[Maxn * 4],
	tag[Maxn * 4],ans,
	T,Z,zl[Maxn];
bool bz[Maxn];
struct node
{
	ll a,b;
	node(){}
	node(ll x,ll y)
	{
		a = y;
		b = last[x];
	}
}a[Maxn * 2],c[Maxn];
ll lowbit(ll x)
{
	return x&-x;
}
void add(ll x,ll y)
{
	a[++a[0].a] = node(x,y);
	last[x] = a[0].a;
}
void change(ll x,ll y)
{
	ll i = x;
	while (i <= n)
	{
		f[i] += y;
		i += lowbit(i);
	}
}
ll sum(ll x)
{
	ll y = 0;
	while (x > 0)
	{
		y += f[x];
		x -= lowbit(x);
	}
	return y;
}
void dfs1(ll x, ll y)
{
	ll i = last[x];
	fa[x] = y;
	for (ll i = last[x]; i; i = a[i].b)
	{
		if (a[i].a != y)
		{
			deep[a[i].a] = deep[x] + 1;
			dfs1(a[i].a,x);
			size[x] += size[a[i].a];
		}
	}
	size[x]++;
	return;
}
void dfs2(ll x,ll y)
{
	ll i,z = 0;
	v[x] = ++T;
	for (i = last[x]; i; i = a[i].b)
	{
		if (a[i].a != y)
		{
			if (size[z] <= size[a[i].a])
				z = a[i].a;
		}
	}
	if (z != 0)
	{
		top[z] = top[x];
		dfs2(z,x);
	}
	for (i = last[x]; i; i = a[i].b)
	{
		if (a[i].a != y && a[i].a != z)
		{
			top[a[i].a] = a[i].a;
			dfs2(a[i].a,x);
		}
	}
}
ll lca(ll x, ll y)
{
	ll  z1 = x,
		z2 = y;
	while (top[x] != top[y])
	{
		if (deep[top[x]] < deep[top[y]])
			swap(x,y);
		ans += sum(v[x]) - sum(v[top[x]] - 1);
		x = fa[top[x]];
	}
	if (deep[x] < deep[y]) swap(x,y);
	ans += sum(v[x]) - sum(v[y]);
	return y;
}
ll lca1(ll x, ll y)
{
	ll  z1 = x,
		z2 = y;
	while (top[x] != top[y])
	{
		if (deep[top[x]] < deep[top[y]])
			swap(x,y);
		x = fa[top[x]];
	}
	if (deep[x] < deep[y])
		swap(x,y);
	if (!bz[y])
	{
		Z += b[y];
		bz[y] = 1;
		zl[++zl[0]] = y;
		change(v[y],-b[y]);
	}
	return y;
}
bool cmp(node a, node b)
{
	return a.b < b.b;
}
int main()
{
	ll i,j,k;
	memset(tag, -1, sizeof(tag));
	scanf("%lld%lld", &n, &m);
	for (i = 1; i <= n; i++)
	{
		scanf("%lld", &b[i]);
	}
	for (i = 1; i < n; i++)
	{
		ll x,y;
		scanf("%lld%lld", &x, &y);
		add(x,y);
		add(y,x);
	}
	dfs1(1,0);
	top[1] = 1;
	dfs2(1,0);
	for (i = 1; i <= n; i++)
	{
		change(v[i],b[i]);
	}
	for (i = 1; i <= m; i++)
	{
		scanf("\n");
		char ch = getchar();
		if (ch == 'Q')
		{
			ll x;
			scanf("%lld", &x);
			c[0].a = 0;
			while (x)
			{
				c[++c[0].a].a = x;
				c[c[0].a].b = v[x];
				scanf("%lld", &x);
			}
			if (c[0].a == 1)
			{
				printf("%lld\n",b[c[1].a]);
				continue;
			}
			sort(c + 1, c + c[0].a + 1, cmp);
			c[++c[0].a].a = c[1].a;
			ans = Z = zl[0] = 0;
			for (j = 1; j <= c[0].a - 1; j++)
			{
				lca1(c[j].a, c[j + 1].a);
			}
			for (j = 1; j <= c[0].a - 1; j++)
			{
				lca(c[j].a, c[j + 1].a);
			}
			printf("%lld\n",(ans + Z * 2) / 2);
			for (j = 1; j <= zl[0]; j++)
			{
				bz[zl[j]] = false;
				change(v[zl[j]], b[zl[j]]);
			}
		}
		else
		{
			ll x,y;
			scanf("%lld%lld", &x, &y);
			change(v[x], y - b[x]);
			b[x] = y;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ghost_Pig/article/details/83188419