[ZJOI2015]幻想乡战略游戏

已更新

一句话题意:给棵树,有点权有边权

单点修改点权,维护带权重心

观察了各位dalao的博客依旧是没看透彻。。。

最后观察了gxz大佬把代码写完的...

我们先跑一遍点分治,建点分树(代码里的father数组)

跑一遍dfs,预处理倍增lca求dis(代码里的fa,depth,dis数组)

对于每个点,我们维护三个值

num[x]代表x的点分树中所有节点权值和

(num_x=sum_{son}val_{son})

va[x]代表x的点分树的子树中所有节点的权值乘以这个节点到x的距离和

(va_x=sum_{son}dis(x,son)*val_{son})

vb[x]代表x的点分树中的子树中所有节点的权值乘上这个点到x点分树父亲的距离和

(vb_x=sum_{son}dis(father[x],son)*val_{son})

我们维护这三个数组,就可以快速求出在一个点安营扎寨的费用了

如下代码:我们在点分树上暴力跳父亲

每次加上某个点子树内经过这个点到达x的距离

但是由于下一步跳父亲后会被重复统计

我们需要再减去这个点所有儿子经过这个点的点分树父亲到达x的距离

如下代码

long long calc(int x)
{
	long long res = 0;
	for (int y = x; y != 0; y = father[y])
	{
		res += va[y] + num[y] * (long long)getdis(x, y);
		if (father[y] != 0)
			res -= vb[y] + num[y] * (long long)getdis(x, father[y]);
	}
	return res;
}

考虑修改:

直接暴力跳点分树父亲,根据定义修改即可

考虑查询:

我们推式子可以发现在答案在原树上是以一种趋势在链上变化的

所以我们可以在点分树上从点分树的根开始暴力找答案

我们枚举当前节点原树上所有连边

找到最优的地方,向那个方向的点分树根跳(因为保证最优解一定在那个方向的子树里)

如果不存在,说明当前点为根,直接返回答案即可

链式前向星多开了一个变量rt表示向这个方向如果跳点分树儿子会跳到哪个儿子(参考GXZ代码)

long long query()
{
	int x = root0;
	long long sb = calc(x), t;
	while (1)
	{
		int y = x;
		sb = calc(x);
		for (int i = h[x]; i != 0; i = a[i].ne)
			if (a[i].rt && (t = calc(a[i].v)) < sb)
				sb = t, y = a[i].rt;
		if (x == y)
			break;
		x = y;
	}
	return sb;
}

时间复杂度本来是(O(nlog^2n)),但是由于我用的倍增LCA,是(O(nlog^3n))

代码里偷懒没写快读,还用的倍增LCA,开O2在洛谷上勉强卡过,不开O2T得飞起。。30分。。。

本机测试,不开O2最大的点9s 开了3s

乱七八糟的代码

#include <bits/stdc++.h>
using namespace std;

struct edge
{
	int v, w, ne, rt;
} a[200010];

int h[100010], tmp;
int sz[100010], maxw[100010];
int father[100010];
bool vis[100010];
int fa[100010][18], depth[100010], dis[100010];
long long va[100010], num[100010], vb[100010];
int n, q, root, sum, root0;

void add(int x, int y, int z)
{
	a[++tmp] = (edge){y, z, h[x]};
	h[x] = tmp;
}

void getroot(int x, int fa)
{
	sz[x] = 1;
	maxw[x] = 0;
	for (int i = h[x]; i != 0; i = a[i].ne)
		if (fa != a[i].v && vis[a[i].v] == false)
		{
			getroot(a[i].v, x);
			sz[x] += sz[a[i].v];
			maxw[x] = max(maxw[x], sz[a[i].v]);
		}
	maxw[x] = max(maxw[x], sum - sz[x]);
	if (maxw[x] < maxw[root])
		root = x;
}

void getsz(int x, int fa)
{
	sz[x] = 1;
	for (int i = h[x]; i != 0; i = a[i].ne)
		if (fa != a[i].v && vis[a[i].v] == false)
			getsz(a[i].v, x), sz[x] += sz[a[i].v];
}

void solve(int x)
{
	vis[x] = true;
	getsz(x, 0);
	for (int i = h[x]; i != 0; i = a[i].ne)
		if (vis[a[i].v] == false)
			sum = sz[a[i].v], root = 0, getroot(a[i].v, 0), a[i].rt = root, father[root] = x, solve(root);
}

void dfs(int x)
{
	for (int i = h[x]; i != 0; i = a[i].ne)
		if (fa[x][0] != a[i].v)
			fa[a[i].v][0] = x, depth[a[i].v] = depth[x] + 1, dis[a[i].v] = dis[x] + a[i].w, dfs(a[i].v);
}

int lca(int x, int y)
{
	if (x == 0 || y == 0)
		return 0;
	if (depth[x] < depth[y])
		swap(x, y);
	int fuck = depth[x] - depth[y];
	for (int i = 17; i >= 0; i--)
		if (fuck & (1 << i))
			x = fa[x][i];
	if (x == y)
		return x;
	for (int i = 17; i >= 0; i--)
		if (fa[x][i] != fa[y][i])
			x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

int getdis(int x, int y)
{
	return dis[x] + dis[y] - 2 * dis[lca(x, y)];
}

long long calc(int x)
{
	long long res = 0;
	for (int y = x; y != 0; y = father[y])
	{
		res += va[y] + num[y] * (long long)getdis(x, y);
		if (father[y] != 0)
			res -= vb[y] + num[y] * (long long)getdis(x, father[y]);
	}
	return res;
}

long long query()
{
	int x = root0;
	long long sb = calc(x), t;
	while (1)
	{
		int y = x;
		sb = calc(x);
		for (int i = h[x]; i != 0; i = a[i].ne)
			if (a[i].rt && (t = calc(a[i].v)) < sb)
				sb = t, y = a[i].rt;
		if (x == y)
			break;
		x = y;
	}
	return sb;
}

int main()
{
	scanf("%d%d", &n, &q);
	for (int x, y, z, i = 1; i < n; i++)
		scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
	maxw[0] = n, sum = n, getroot(1, 0), root0 = root, solve(root);
	dfs(1);
	for (int j = 1; j <= 17; j++)
		for (int i = 1; i <= n; i++)
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
	for (int x, chenge, i = 1; i <= q; i++)
	{
		scanf("%d%d", &x, &chenge);
		for (int y = x; y != 0; y = father[y])
		{
			va[y] += chenge * (long long)getdis(x, y), num[y] += chenge;
			if (father[y] != 0)
				vb[y] += chenge * (long long)getdis(x, father[y]);
		}
		printf("%lld
", query());
	}
	return 0;
}
原文地址:https://www.cnblogs.com/oier/p/10235898.html