首页 > 其他分享 >「题解注释」P3345 [ZJOI2015] 幻想乡战略游戏

「题解注释」P3345 [ZJOI2015] 幻想乡战略游戏

时间:2023-08-14 17:01:05浏览次数:41  
标签:rs int 题解 sum tag ZJOI2015 LL P3345 dis

题解 P3345 【[ZJOI2015]幻想乡战略游戏】 - Baka's Blog - 洛谷博客 (luogu.org)

耗时:半个下午

代码注释:

#include <bits/stdc++.h>
typedef long long LL;

inline int rd() {
	int a = 1, b = 0; char c = getchar();
	while (!isdigit(c)) a = c == '-' ? 0 : 1, c = getchar();
	while (isdigit(c)) b = b * 10 + c - '0', c = getchar();
	return a ? b : -b;
}

const int N = 1e5 + 233;
int n, m;

struct Graph { int to, nxt, cost; } g[N * 2];
int head[N], tot;

inline void addedge(int x, int y, int c) {
	g[++tot].to = y, g[tot].nxt = head[x],
	g[tot].cost = c, head[x] = tot;
}

int fa[N], son[N], size[N], dep[N], dis[N];
int top[N], id[N], wh[N], wt[N], num;

void dfs1(int x) {
	for (int i = head[x]; i; i = g[i].nxt) {
		int y = g[i].to;
		if (y != fa[x]) {
			fa[y] = x;
			dep[y] = dep[x] + 1;
			dis[y] = dis[x] + g[i].cost;
			size[y] = 1;
			dfs1(y);
			if (size[son[x]] < size[y])
				son[x] = y;
			size[x] += size[y];
		}
	}
}

void dfs2(int x, int topf) {
	top[x] = topf; // 链顶
	id[x] = ++num; // 在线段树上的位置
	wh[num] = x; // 线段树上这个位置的元素
	wt[num] = dis[x] - dis[fa[x]]; // 当前节点到父节点的距离
	if (son[x]) {
		dfs2(son[x], topf);
		for (int i = head[x]; i; i = g[i].nxt) {
			int y = g[i].to;
			if (y != fa[x] && y != son[x])
				dfs2(y, y);
		}
	}
}

#define ls(p) p << 1
#define rs(p) p << 1 | 1

int sz[N * 4], tag[N * 4];
LL edge[N * 4], sum[N * 4];

void build(int p, int L, int R) {
	if (L == R) {
		edge[p] = wt[L];
		return;
	}
	int mid = (L + R) >> 1;
	build(ls(p), L, mid);
	build(rs(p), mid + 1, R);
	edge[p] = edge[ls(p)] + edge[rs(p)];
}

inline void pushup(int p) {
	sz[p] = std::max(sz[ls(p)], sz[rs(p)]);
	sum[p] = sum[ls(p)] + sum[rs(p)];
}

inline void pushdown(int p) {
	if (tag[p]) {
		sz[ls(p)] += tag[p];
		sz[rs(p)] += tag[p];
		tag[ls(p)] += tag[p];
		tag[rs(p)] += tag[p];
		sum[ls(p)] += (LL)tag[p] * edge[ls(p)];
		sum[rs(p)] += (LL)tag[p] * edge[rs(p)];
		tag[p] = 0;
	}
}

void add(int p, int l, int r, int v, int L, int R) { // 处理到新增加的长度
	if (l <= L && r >= R) {
		sz[p] += v; tag[p] += v;
		sum[p] += (LL)v * edge[p];
		return;
	}
	pushdown(p);
	int mid = (L + R) >> 1;
	if (l <= mid)
		add(ls(p), l, r, v, L, mid);
	if (r > mid)
		add(rs(p), l, r, v, mid + 1, R);
	pushup(p);
}

LL query(int p, int l, int r, int L, int R) { // 查询总长度
	if (l <= L && r >= R)
		return sum[p];
	pushdown(p);
	int mid = (L + R) >> 1;
	LL ret = 0;
	if (l <= mid)
		ret += query(ls(p), l, r, L, mid);
	if (r > mid)
		ret += query(rs(p), l, r, mid + 1, R);
	return ret;
}

inline int weight() { // 线段树上二分找重心
	int p = 1, L = 1, R = n;
	while (L < R) {
		pushdown(p);
		int mid = (L + R) >> 1;
		if (sz[rs(p)] * 2 >= sz[1])
			L = mid + 1, p = rs(p);
		else R = mid, p = ls(p);
	}
	return wh[L];
}

inline void range_add(int x, int y) { // 修改到链顶的 dis 总和
	while (top[x] != 1) {
		add(1, id[top[x]], id[x], y, 1, n);
		x = fa[top[x]];
	}
	add(1, 1, id[x], y, 1, n);
}

inline LL range_query(int x) { // 查询到链顶的 dis 总和,可以参考 lca 的深度求法
	LL ret = 0;
	while (top[x] != 1) {
		ret += query(1, id[top[x]], id[x], 1, n);
		x = fa[top[x]];
	}
	return ret + query(1, 1, id[x], 1, n);
}

LL sum_dis_e, sum_e;

inline LL getans(int x) {
	// dis[x, root] * e[y] + dis[rt, root] * e[y] - 2 * range(x, root)
	return sum_dis_e + dis[x] * sum_e - 2 * range_query(x);
}

int main() {
	n = rd(); m = rd();
	for (int i = 1; i < n; ++i) {
		int x = rd(), y = rd(), c = rd();
		addedge(x, y, c);
		addedge(y, x, c);
	}
	
	dfs1(1); // 求重儿子的 dfs
	dfs2(1, 1); // 重链剖分二阶段
	build(1, 1, n); // “摆树枝”
	
	while (m--) {
		int x = rd(), y = rd(); // 在 x 点上放 y 个军队
		sum_e += y; // 军队总和
		sum_dis_e += (LL)dis[x] * y; // 求总和
		range_add(x, y);
		printf("%lld\n", getans(weight()));
	}
	return 0;
}

标签:rs,int,题解,sum,tag,ZJOI2015,LL,P3345,dis
From: https://www.cnblogs.com/yifan0305/p/17629145.html

相关文章

  • CF1859C 题解
    思路我们实际上发现它计算的就是\(p_i\cdoti\)的和再减去一个\(p_i\cdoti\)中的最大值。那我们可以枚举这个最大值\(p_x\cdotx\),这个值就是最后和中需要删除的数值。这里我们可以使用贪心。我们可以从\(n\sim1\)枚举除\(p_i\)的每个数字需要配的数字。当然,......
  • CF1859B 题解
    题意给定\(n\)个长度为\(m\)的数组,每个数组可以向别的数组转移最多一个数字,任意一个数组都可以接受无穷多的数字,最大化每个数组的最小值之和。做法考虑贪心。我们记第\(i\)个数组的第\(j\)个数字为\(a_{i,j}\)。我们先对每一个数组按照升序进行排序,那我们最不愿意......
  • 【题解】 Call Me Call Me CCPC Mianyang 2022
    https://codeforces.com/gym/104065/原题做法是类似猫树转成前缀后缀,写起来太麻烦,不如如下做法:如果每个区间所需满足的点不超过\(\sqrt{n}\)个,即可以如下暴力:把每个区间拍到线段树上,每次更新一个点,则在线段树上把所有包含他的区间全部\(-1\)看看是否减到了\(0\),拿个队列一......
  • 问题解答:关于 SAP UI5 控制器(Controller) JavaScript 编码里单引号和双引号的用法澄
    笔者这篇教程文末,有朋友提问:SAPUI5应用开发教程之十-什么是SAPUI5应用的描述符文件manifest.json问题1:在index.html文件中body标签添加了代码:<divdata-sap-ui-componentdata-name="sap.ui5.walkthrough"data-id="container"data-settings='{"id":"wa......
  • ARC129C 题解
    problem&blog。提供一种不一样的做法喵。考虑原问题的逆问题。这个很典,直接前缀和\(sum_i\)表示\([1,i]\)顺次拼接的数\(\bmod7\)的值,那么\([l,r]\)符合条件当且仅当\(sum_r-sum_{l-1}=0\),即\(sum_r=sum_{l-1}\)。设\(c_p=\sum\limits_{i=1}^n[sum_i=p]\),这个逆......
  • 【题解】洛谷 P9532 [YsOI2023] 前缀和
    原题链接【LGR-151-Div.2】洛谷8月月赛II&YsOI2023T1解题思路设有一序列a,其中a1 =a2,第k(≥3) 项为前k-1项的前缀和。可以发现前q项分别为第一项的20 倍,20 倍,21 倍,22 倍,23 倍…2q-3 倍,2q-2 倍。扩展到整个序列中,可得第i( ≥ 3)项一定为2i-2a1。......
  • 【LSOIT1】秒速,五厘米 ----题解
    【LSOIT1】秒速,五厘米----题解题目传送门【明里。】【贵树君。】【明年,也能一起看樱花吗?】【昨天,我做了一个梦,在梦里,我们都才十三岁。那是覆盖着厚厚的一层白雪的田园。】【民家的灯火遥不可及,只能看到零星的两点。】很显然,这是一道签到题,出题人非常良心。题目大意:从......
  • 【LSOIT2】言叶之庭 ---题解
    【LSOIT2】言叶之庭---题解题目传送门【你肯定怀疑我有问题吧。】【没有。】【我不介意呀,反正人类,多多少少有点不正常的。】【我知道这不正常,但真的很喜欢设计鞋子,当然,水平还不够。】【不知不觉,我都在期待雨天。晴天里,总觉得被困在孩子气里的世界,焦虑无比。】【在我看来......
  • 【LSOIT3】天气之子 ---题解
    【LSOIT3】天气之子---题解题目传送门【我叫阳菜。请多关照,帆高。】【她一直不断的祈祷着,一边不断地穿过那个鸟居。】【我做了个梦,初见你时,就像是迷途的小猫一样。】【而你却帮我找到了存在的意义。】【呐,马上要开始放晴了哦。】这个题其他做法不知道怎么搞,暴力的话也不......
  • 洛谷P9533 区间翻转区间异或和 题解
    原题:洛谷P9533一道性质题不难发现,区间翻转操作是没有用的(虽然比赛的时候想了好久www)首先,区间翻转要想对答案有贡献,一定是下边这种情况:三个连续的区间:\(A~|~B~|~C\)满足:\(B\oplusC=0,A\oplusC=0\)。将\(B∪C\)这个灵异区间进行翻转,使\(A\)和\(C\)合并到一起,会增加一......