首页 > 其他分享 >Luogu P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

Luogu P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

时间:2023-06-07 11:55:21浏览次数:39  
标签:int Luogu tree htree P4556 Vani 救济粮 now id

[Vani有约会]雨天的尾巴 /【模板】线段树合并

题目背景

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

题目描述

首先村落里的一共有 \(n\) 座房屋,并形成一个树状结构。然后救济粮分 \(m\) 次发放,每次选择两个房屋 \((x, y)\),然后对于 \(x\) 到 \(y\) 的路径上(含 \(x\) 和 \(y\))每座房子里发放一袋 \(z\) 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

输入格式

输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 \(n\) 和救济粮发放的次数 \(m\)。

第 \(2\) 到 第 \(n\) 行,每行有两个用空格隔开的整数 \(a, b\),代表存在一条连接房屋 \(a\) 和 \(b\) 的边。

第 \((n + 1)\) 到第 \((n + m)\) 行,每行有三个用空格隔开的整数 \(x, y, z\),代表一次救济粮的发放是从 \(x\) 到 \(y\) 路径上的每栋房子发放了一袋 \(z\) 类型的救济粮。

输出格式

输出 \(n\) 行,每行一个整数,第 \(i\) 行的整数代表 \(i\) 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。

如果某座房屋没有救济粮,则输出 \(0\)。

样例 #1

样例输入 #1

5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3

样例输出 #1

2
3
3
0
2

提示

  • 对于 \(20\%\) 的数据,保证 \(n, m \leq 100\)。
  • 对于 \(50\%\) 的数据,保证 \(n, m \leq 2 \times 10^3\)。
  • 对于 \(100\%\) 测试数据,保证 \(1 \leq n, m \leq 10^5\),\(1 \leq a,b,x,y \leq n\),\(1 \leq z \leq 10^5\)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define lid (tree[id].lson)
#define rid (tree[id].rson)

using namespace std;

int n, m;
int cnt, head[5211314], nex[5211314], to[5211314];
int rank_[5211314], new_id;
int root[5211314];
int out[5211314];

struct Heavy_Light_Tree {
	int deep;
	int father;
	int son;
	int id;
	int size;
	int top;
}htree[5211314];

struct Segment_Tree {
	int lson, rson;
	int maxn, color;
}tree[52113140];

inline int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch - '0');
		ch = getchar();
	}
	return f * x;
}

inline void AddPoi(int v1, int v2) {
	++ cnt;
	nex[cnt] = head[v1];
	head[v1] = cnt;
	to[cnt] = v2;
	return;
}

void DfsSon(int now, int father) {
	htree[now].deep = htree[father].deep + 1;
	htree[now].father = father;
	htree[now].size = 1;
	for (int i = head[now]; i != 0; i = nex[i]) {
		if (to[i] == father) continue;
		DfsSon(to[i], now);
		htree[now].size += htree[to[i]].size;
		if (htree[to[i]].size > htree[htree[now].son].size) {
			htree[now].son = to[i];
		}
	}
	return;
}

void DfsTop(int now, int top) {
	htree[now].top = top;
	if (htree[now].son != 0) DfsTop(htree[now].son, top);
	for (int i = head[now]; i != 0; i =nex[i]) {
		if (to[i] != htree[now].father && to[i] != htree[now].son) {
			DfsTop(to[i], to[i]);
		}
	}
	return;
}

inline int LCA(int x, int y) {
	while (htree[x].top != htree[y].top) {
		if (htree[htree[x].top].deep < htree[htree[y].top].deep) swap(x, y);
		x = htree[htree[x].top].father;
	}
	if (htree[x].deep > htree[y].deep) swap(x, y);
	return x;
}

class Segment {
	private:
		void Pushup(int id) {
			if (tree[lid].maxn < tree[rid].maxn) {
				tree[id].maxn = tree[rid].maxn;
				tree[id].color = tree[rid].color;
			}
			else {
				tree[id].maxn = tree[lid].maxn;
				tree[id].color = tree[lid].color;
			}
		}
	public:
		int Update(int id, int l, int r, int color, int number) {
			//单点更新 
			if (id == 0) id = ++ cnt;
			if (l == r) {
				tree[id].maxn += number;
				tree[id].color = color;
				return id;
			}
			int mid = (l + r) >> 1;
			if (color <= mid) lid = Update(lid, l, mid, color, number);
			else rid = Update(rid, mid + 1, r, color, number);
			Pushup(id);
			return id;
		}
		int Merge(int a, int b, int l, int r) {
			if (a == 0) return b;
			if (b == 0) return a;			
			// 返回非空树  
			if (l == r) {
				tree[a].maxn += tree[b].maxn;
				return a;
			}
			int mid = (l + r) >> 1;
			tree[a].lson = Merge(tree[a].lson, tree[b].lson, l, mid);
			tree[a].rson = Merge(tree[a].rson, tree[b].rson, mid + 1, r);
			Pushup(a);
			return a;
		}
}ask;

void operate(int now) {
	for (int i = head[now]; i != 0; i = nex[i]) {
		if (to[i] == htree[now].father) continue;
		operate(to[i]);
		root[now] = ask.Merge(root[now], root[to[i]], 1, 100000); 
	}
	if (tree[root[now]].maxn == 0) out[now] = 0;
	else out[now] = tree[root[now]].color;
	return;
}

int main() {
	n = read();
	m = read();
	for (int i = 1, a, b; i <= n - 1; ++ i) {
		a = read();
		b = read();
		AddPoi(a, b);
		AddPoi(b, a);
	}
	DfsSon(1, 0);
	DfsTop(1, 1);
	for (int i = 1, x, y, z, lca; i <= m; ++ i) {
		x = read();
		y = read();
		z = read();
		root[x] = ask.Update(root[x], 1, 100000, z, 1);
		root[y] = ask.Update(root[y], 1, 100000, z, 1);
		//树上差分 树上查询的两点的值+1,两点间的lca的值-1,lca的fa的值减一
		//         每个节点表示被经过几次 
		lca = LCA(x, y);
		root[lca] = ask.Update(root[lca], 1, 100000, z, -1);
		root[htree[lca].father] = ask.Update(root[htree[lca].father], 1, 100000, z, -1);
	}
	operate(1);
	for (int i = 1; i <= n; ++ i) {
		printf("%d\n", out[i]);
	}
	return 0;
}

标签:int,Luogu,tree,htree,P4556,Vani,救济粮,now,id
From: https://www.cnblogs.com/jueqingfeng/p/17462959.html

相关文章

  • LuoguP4318 完全平方数
    标签:莫比乌斯函数,容斥完全平方数题目描述小X自幼就很喜欢数。但奇怪的是,他十分讨厌完全平方数。他觉得这些数看起来很令人难受。由此,他也讨厌所有是完全平方数的正整数倍的数。然而这丝毫不影响他对其他数的热爱。这天是小X的生日,小W想送一个数给他作为生日礼物。当然他......
  • Luogu P2709 小B的询问
    https://www.luogu.com.cn/problem/P2709#submit小B的询问题目描述小B有一个长为\(n\)的整数序列\(a\),值域为\([1,k]\)。他一共有\(m\)个询问,每个询问给定一个区间\([l,r]\),求:\[\sum\limits_{i=1}^kc_i^2\]其中\(c_i\)表示数字\(i\)在\([l,r]\)中的出现次数......
  • [刷题笔记] Luogu P2895 Meteor Shower S
    ProblemSolution显然bfs,只不过有了限定条件,有实时的流星雨这里提供两种做法:Solution1这也是我一开始的做法模拟实时流星,由于bfs是按层搜的,是严格按照时间递增搜的,故可以模拟实时放流星。需要注意放流星的时间,如果第\(t\)秒有流星,则该秒不可以走,需要在每一秒前放流星。那......
  • [刷题笔记] LuoguP2658 汽车拉力比赛
    ProblemSolution需要找到最小满足题意的\(d\),显然\(d\)满足单调性,考虑二分二分\(d\),然后直接bfs,每次bfs判断能不能走的时候还需要加上高度差不超过二分的\(d\)(即满足),bfs跑完后看看所有的路标都被访问了没。(可以记录个数,因为不可能重复走)二分的时候注意\(l\)从0开始,不然会WA......
  • luogu P8497 [NOI2022] 移除石子
    题面传送门不好评价?首先我们考虑最基础的情况,当\(k=0,l_i=r_i\)时,相当于我们需要判定一个状态能不能被消完。这相当于我们要执行若干次\(2\)操作,使得每个位置要么大于等于\(2\),要么为\(0\)。为此我们需要挖掘一些操作\(2\)的性质。性质\(1\):操作区间长度不会超过\(5......
  • Luogu P1007 独木桥
    题目描述link思路找到独木桥的中间位置,最少时间考虑在端点左侧的,向左走,在端点右侧的向右走.最多时间考虑在端点左侧的向右走,在端点右侧的向左走.最少时间即为最优情况下最多的时间,最多时间即为最差情况下的最多时间Code#include<cstdio>#include<algorithm>......
  • Luogu P1008 三连击
    题目描述link思路因为\(1-9\)且不能重复使用,所以从\(123\)循环至\(789\),相应的\(2\)倍,\(3\)倍,即为另两个数字.对每个数字进行拆分,所用数字使用次数\(+1\),判断是否每个数字都被使用且只使用一次,输出即可.Code#include<cstdio>#include<cstring>i......
  • 最小生成树_LuoguP1669
    P1669P1669[USACO04DEC]BadCowtractorsS题目传送门题意简化:在一个有\(N\)个点\(M\)条边的图中选出\(N-1\)条边构成一棵树,使得树的总边权最大,求最大总边权。上述问题即为最小(大)生成树问题,本题为最大生成树,如有未详者可以移步P3366。该问题一般是Kruskal和Prim......
  • Luogu P1903 [国家集训队] 数颜色 / 维护队列
    题目来源https://www.luogu.com.cn/problem/P1903[国家集训队]数颜色/维护队列题目描述墨墨购买了一套\(N\)支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:\(Q\L\R\)代表询问你从第\(L\)支画笔到第\(R\)支画笔中共有几种......
  • 刷题笔记:Luogu P3956 棋盘
    ProblemSolutionDFS/BFS需要注意去重的时候可以重复走(因为有限定条件),只要新的步数比原来的步数小就可以走,其余情况模拟即可细节有点多,比如需要记录一下上一步的棋盘颜色(下一次搜索传递参数),因为牵扯到使用魔法问题,不能直接染,因为改变地图后后边很多操作都会受影响在列举可能性......