P3345 [ZJOI2015]幻想乡战略游戏 动态点分治

(color{#0066ff}{ 题目描述 })

傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。

在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。

在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv*dist(u,v)的金钱来补给这些军队。

由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。

因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。

但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。

(color{#0066ff}{输入格式})

第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。数据保证任何时刻每个点上的军队数量都是非负的。

(color{#0066ff}{输出格式})

对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。

(color{#0066ff}{输入样例})

10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1

(color{#0066ff}{输出样例})

0
1
4
5
6

(color{#0066ff}{数据范围与提示})

对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 非常神奇的是,对于所有数据,这棵树上的点的度数都不超过20,且N,Q>=1

(color{#0066ff}{题解})

这题既然给出了大方的6s,一定是道巨暴力题

动态点分治题

建立点分树,每个点维护三个值

gather[i]代表点分树中以i为根子树的所有点的军队数量 * 该点到i的dis(注意是原树上的dis)

tofa[i]表示点分树中以i为根的子树对它在点分树上的父亲的gather的贡献

tot[i]代表点分树中以i为根的子树中的军队数量

考虑当前补给站在a, 考虑移动到a的儿子b

那么a除了b的所有子树的军队都会加上一个(dis_{ab})

b的子树内所有的军队都会减去一个(dis_{ab})

可以发现,如果某个点更优,当且仅当外面的军队少,里面的军队多

所以一旦发现b更优,那么最后的补给站一定在b子树内

所以对于询问,我从点分树的根开始找,一旦发现孩子更优,就在孩子的子树中递归

现在考虑怎么计算一个点的答案(就是补给站在那个点的答案)

首先,初始答案是gather[i],这是子树内的贡献

现在考虑怎么统计子树外的点的贡献

设dis为当前点到补给站的距离(原树距离)

用tot[fa[i]] - tot[i]就是父亲子树中出去当前子树的部分的军队数量

用这个乘上一个dis,就是父亲与当前点的路径的贡献

这是父亲到当前点的,还有一段是军队到父亲的(军队到父亲+父亲到当前点=军队到当前点)

这部分为gather[fa[i]] - tofa[i]父亲的总贡献- 当前子树贡献就是其它点到父亲的贡献

一直统计就行了

对于修改,在点分树上向上跳(log)然后直接维护三个值就行了

(O(nlog^2n))

这是写欧拉序RMQ查询距离的复杂度

如果写倍增,那复杂度就是(O(nlog^3n)),会TLE

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; int x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}

struct node {
	int to;
	LL dis;
	node *nxt;
	node(int to = 0, LL dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
	void *operator new (size_t) {
		static node *S = NULL, *T = NULL;
		return (S == T) && (T = (S = new node[1024]) + 1024), S++;
	}
};
const int maxn = 2e5 + 10;
int siz[maxn], f[maxn], root, sum, n, q, rt, fa[maxn], lg[maxn];
LL gather[maxn], tofa[maxn],tot[maxn];
LL st[maxn][26], dfn[maxn], euler, Dis[maxn];
bool vis[maxn];
node *h[maxn], *head[maxn];
void add(int from, int to, int dis, node **hh) {
	hh[from] = new node(to, dis, hh[from]);
}
void dfs(int x, int F, LL dis) {
	Dis[x] = dis;
	st[++euler][0] = dis, dfn[x] = euler;
	for(node *i = h[x]; i; i = i->nxt) {
		if(i->to == F) continue;
		dfs(i->to, x, dis + i->dis);
		st[++euler][0] = dis;
	}
}
void ST() {
	dfs(1, 0, 0);
	lg[0] = -1;
	for(int i = 1; i < maxn; i++) lg[i] = lg[i >> 1] + 1;
	for(int j = 1; (1 << j) <= euler; j++)
		for(int i = 1; i + (1 << j) - 1 <= euler; i++)
			st[i][j] = std::min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
LL getdis(int x, int y) {
	if(dfn[x] > dfn[y]) std::swap(x, y);
	int len = lg[dfn[y] - dfn[x] + 1];
	return Dis[x] + Dis[y] - (std::min(st[dfn[x]][len], st[dfn[y] - (1 << len) + 1][len]) << 1);
}

void getroot(int x, int F) {
	siz[x] = 1;
	f[x] = 0;
	for(node *i = h[x]; i; i = i->nxt) {
		if(i->to == F || vis[i->to]) continue;
		getroot(i->to, x);
		siz[x] += siz[i->to];
		f[x] = std::max(f[x], siz[i->to]);
	}
	f[x] = std::max(f[x], sum - siz[x]);
	if(f[x] < f[root]) root = x;
}

void build(int x) {
	vis[x] = true;
	for(node *i = h[x]; i; i = i->nxt) {
		if(vis[i->to]) continue;
		sum = siz[i->to], root = 0;
		getroot(i->to, x);
		add(x, root, i->to, head);
		fa[root] = x;
		build(root);
	}
}
void add(int pos, LL val) {
	tot[pos] += val;
	for(int i = pos; fa[i]; i = fa[i]) {
		LL D = getdis(pos, fa[i]);
		tofa[i] += D * val;
		tot[fa[i]] += val;
		gather[fa[i]] += D * val;
	}
}
LL calc(int x) {
	LL res = gather[x];
	for(int i = x; fa[i]; i = fa[i]) {
		LL D = getdis(fa[i], x);
		res += (tot[fa[i]] - tot[i]) * D;
		res += gather[fa[i]] - tofa[i];
	}
	return res;
}
LL query(int x) {
	LL res = calc(x);
	for(node *i = head[x]; i; i = i->nxt) 
		if(calc(i->dis) < res) return query(i->to);
	return res;
}
		
signed main() {
	n = in(), q = in();
	int x, y, z;
	for(int i = 1; i < n; i++) {
		x = in(), y = in(), z = in();
		add(x, y, z, h), add(y, x, z, h);
	}
	f[0] = sum = n;
	getroot(1, 0);
	int rt = root;
	build(root);
	ST();
	while(q --> 0) {
		x = in(), y = in();
		add(x, y);
		printf("%lld
", query(rt));
	 }
	return 0;
}
原文地址:https://www.cnblogs.com/olinr/p/10223262.html