首页 > 其他分享 >Luogu-P4315 月下“毛景树”

Luogu-P4315 月下“毛景树”

时间:2023-09-17 10:12:00浏览次数:44  
标签:毛景树 Luogu rson tree cover int add P4315 lson

在洛谷中查看


前言

将边权转化到点权的树剖,很好理解,但我就说说线段树部分。

原本想做 P1505 [国家集训队] 旅游 的,但是发现它需要边权转化点权,所以先做了这题,于是代码里维护了 \(minn\)、\(maxn\)、\(sum\)。

包含内容:线段树部分怎么写、本题随机数据生成器


线段树怎么写

先试着自己写一写

我们肯定要有两个标记:

  • cover 覆盖标记
  • add 加标记

但这两个标记会互相影响的,怎么办呢?

很显然,我们打上覆盖标记时,之前的加标记肯定已经没了(加在覆盖之前,所以清零),所以

void update_cover(int x, int l, int r, int w) {
	if (l <= tree[x].l && tree[x].r <= r) {
		tree[x].sum = siz(x) * w;
		tree[x].minn = tree[x].maxn = w;
		tree[x].cover = w;
		tree[x].add = 0;//直接清空
		return;
	}
	pushdown(x);
	if (l <= tree[lson].r)update_cover(lson, l, r, w);
	if (r >= tree[rson].l)update_cover(rson, l, r, w);
	pushup(x);
}

\(\,\)

那怎么下传标记呢?

因为题目保证了每条树枝上果子的数量都是正数,所以我们设置覆盖标记初始值为 \(-1\)。

然后加操作直接加就行。

还有一个重要的地方:覆盖操作优先级大于加操作,因为打上覆盖标记时,将加标记清零了,所以如果加操作不为 \(0\) 的话,那它一定是在覆盖操作后进行的。

结合代码非常好理解:

void pushdown(int x) {
	//先进行覆盖操作,因为当打上覆盖操作标记时,加标记已经清零了
	if (tree[x].cover != -1) {
		tree[lson].sum = siz(lson) * tree[x].cover;//修改左子树和
		tree[rson].sum = siz(rson) * tree[x].cover;//修改右子树和
		tree[lson].minn = tree[lson].maxn = tree[rson].minn = tree[rson].maxn = tree[x].cover;//修改左右子树最大最小值
		tree[lson].cover = tree[rson].cover = tree[x].cover;//下传覆盖标记
		tree[lson].add = tree[rson].add = 0;//清除加标记,不会误清理,因为如果覆盖后加操作的话,之前是已经下传过了 
		tree[x].cover = -1;//清除覆盖标记
	}
	if (tree[x].add) {
		tree[lson].sum += siz(lson) * tree[x].add;//修改左子树和
		tree[rson].sum += siz(rson) * tree[x].add;//修改右子树和
		tree[lson].minn += tree[x].add;//修改左子树最小值
		tree[lson].maxn += tree[x].add;//修改左子树最大值
		tree[rson].minn += tree[x].add;//修改右子树最小值
		tree[rson].maxn += tree[x].add;//修改右子树最大值
		tree[lson].add += tree[x].add;
		tree[rson].add += tree[x].add;//下传加标记
		tree[x].add = 0;//清除加标记
	}
}

你可能会有一点疑惑:为什么这样就是对的?

其实,线段树懒标记的下传是否正确,那就是有没有考虑到每种懒标记互相影响的情况,那这东西也是要练的(应该吧),所以只要我都考虑到了,那我就是对的。


Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e5 + 5;
int n, m, go[N], dep[N], siz[N], son[N], top[N], dfn[N], num, tmp[N], w[N];
string opt;
struct edge {
	int to, w;
};
vector<edge> g[N];
struct Tmp_edge {
	int u, v;
}tmp_edge[N];
struct segment_tree {
	int l, r, minn, maxn;
	ll sum, cover, add;
	#define lson (x<<1)
	#define rson (x<<1|1)
	#define siz(x) (ll)(tree[x].r - tree[x].l + 1)
}tree[N << 2];
void pushup(int x) {
	tree[x].sum = tree[lson].sum + tree[rson].sum;
	tree[x].minn = min(tree[lson].minn, tree[rson].minn);
	tree[x].maxn = max(tree[lson].maxn, tree[rson].maxn);
}
void build(int x, int l, int r) {
	tree[x].l = l;
	tree[x].r = r;
	tree[x].minn = 0x3f3f3f3f;
	tree[x].maxn = -0x3f3f3f3f;
	tree[x].cover = -1;//不可能是负数,所以赋个负数 
	if (l == r) {
		tree[x].minn = tree[x].maxn = tree[x].sum = w[l];
		return;
	}
	int mid = l + r >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	pushup(x);
}
void pushdown(int x) {
	//先进行覆盖操作,因为当打上覆盖操作标记时,加标记已经清零了
	if (tree[x].cover != -1) {
		tree[lson].sum = siz(lson) * tree[x].cover;//修改左子树和
		tree[rson].sum = siz(rson) * tree[x].cover;//修改右子树和
		tree[lson].minn = tree[lson].maxn = tree[rson].minn = tree[rson].maxn = tree[x].cover;//修改左右子树最大最小值
		tree[lson].cover = tree[rson].cover = tree[x].cover;//下传覆盖标记
		tree[lson].add = tree[rson].add = 0;//清除加标记,不会误清理,因为如果覆盖后加操作的话,之前是已经下传过了 
		tree[x].cover = -1;//清除覆盖标记
	}
	if (tree[x].add) {
		tree[lson].sum += siz(lson) * tree[x].add;//修改左子树和
		tree[rson].sum += siz(rson) * tree[x].add;//修改右子树和
		tree[lson].minn += tree[x].add;//修改左子树最小值
		tree[lson].maxn += tree[x].add;//修改左子树最大值
		tree[rson].minn += tree[x].add;//修改右子树最小值
		tree[rson].maxn += tree[x].add;//修改右子树最大值
		tree[lson].add += tree[x].add;
		tree[rson].add += tree[x].add;//下传加标记
		tree[x].add = 0;//清除加标记
	}
}
void update_cover(int x, int l, int r, int w) {
	if (l <= tree[x].l && tree[x].r <= r) {
		tree[x].sum = siz(x) * w;
		tree[x].minn = tree[x].maxn = w;
		tree[x].cover = w;
		tree[x].add = 0;//直接清空
		return;
	}
	pushdown(x);
	if (l <= tree[lson].r)update_cover(lson, l, r, w);
	if (r >= tree[rson].l)update_cover(rson, l, r, w);
	pushup(x);
}
void update_add(int x, int l, int r, int w) {
	if (l <= tree[x].l && tree[x].r <= r) {
		tree[x].sum += siz(x) * w;
		tree[x].minn += w;
		tree[x].maxn += w;
		tree[x].add += w;
		return;
	}
	pushdown(x);
	if (l <= tree[lson].r)update_add(lson, l, r, w);
	if (r >= tree[rson].l)update_add(rson, l, r, w);
	pushup(x);
}
int query_max(int x, int l, int r) {
	if (l <= tree[x].l && tree[x].r <= r) return tree[x].maxn;
	pushdown(x);
	int max_ans = -0x3f3f3f3f;
	if (l <= tree[lson].r)max_ans = max(max_ans, query_max(lson, l, r));
	if (r >= tree[rson].l)max_ans = max(max_ans, query_max(rson, l, r));
	return max_ans;
}
inline int read() {
	int s = 0, f = 1; char c = getchar();
	while (c < '0' || c>'9') { if (c == '-')f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { s = (s << 1) + (s << 3) + (c ^ 48); c = getchar(); }
	return s * f;
}
void dfs1(int x, int fa) {
	go[x] = fa;
	dep[x] = dep[fa] + 1;
	siz[x] = 1;
	for (int i = 0; i < g[x].size(); i++) {
		int t = g[x][i].to;
		if (t == fa)continue;
		dfs1(t, x);
		tmp[t] = g[x][i].w;
		siz[x] += siz[t];
		if (siz[t] > siz[son[x]])son[x] = t;
	}
}
void dfs2(int x, int t) {
	dfn[x] = ++num;
	w[num] = tmp[x];
	top[x] = t;
	if (!son[x])return;
	dfs2(son[x], t);
	for (int i = 0; i < g[x].size(); i++) {
		int to = g[x][i].to;
		if (to == go[x] || to == son[x])continue;
		dfs2(to, to);
	}
}
void update_cover_chain(int x, int y, int w) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])swap(x, y);
		update_cover(1, dfn[top[x]], dfn[x], w);
		x = go[top[x]];
	}
	if (dep[x] > dep[y])swap(x, y);
	update_cover(1, dfn[x] + 1, dfn[y], w);//边上树剖
}
void update_add_chain(int x, int y, int w) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])swap(x, y);
		update_add(1, dfn[top[x]], dfn[x], w);
		x = go[top[x]];
	}
	if (dep[x] > dep[y])swap(x, y);
	update_add(1, dfn[x] + 1, dfn[y], w);//边上树剖
}
int query_max_chain(int x, int y) {
	int max_ans = -0x3f3f3f3f;
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])swap(x, y);
		max_ans = max(max_ans, query_max(1, dfn[top[x]], dfn[x]));
		x = go[top[x]];
	}
	if (dep[x] > dep[y])swap(x, y);
	if (x != y)max_ans = max(max_ans, query_max(1, dfn[x] + 1, dfn[y]));
	return max_ans;//边上树剖
}
int main() {
	n = read();
	for (int i = 1; i < n; i++) {
		int u = read(), v = read(), w = read();
		tmp_edge[i] = Tmp_edge{ u,v };
		g[u].push_back(edge{ v,w });
		g[v].push_back(edge{ u,w });
	}
	dfs1(1, 1);
	dfs2(1, 1);
	build(1, 1, n);
	while (true) {
		cin >> opt;
		if (opt == "Stop") break;
		if (opt == "Change") {
			int k = read(), w = read();
			update_cover_chain(tmp_edge[k].u, tmp_edge[k].v, w);
		}
		if (opt == "Cover") {
			int u = read(), v = read(), w = read();
			if (u == v)continue;
			update_cover_chain(u, v, w);
		}
		if (opt == "Add") {
			int u = read(), v = read(), w = read();
			if (u == v)continue;
			update_add_chain(u, v, w);
		}
		if (opt == "Max") {
			int u = read(), v = read();
			if (u == v)cout << 0 << endl;
			else printf("%d\n", query_max_chain(u, v));
		}
	}
	return 0;
}

赠送对拍的随机数生成器(很烂):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	freopen("test.in","w",stdout);
	srand(time(0));
	int n = 100, m = 1000;
	printf("%d\n",n);
	for(int i=2;i<=n;i++)printf("%d %d %d\n",rand()%(i-1)+1,i,rand()%100);
	while(m--){
		int opt = rand()%4;
		if(opt == 0)printf("%s %d %d\n","Max",rand()%n+1,rand()%n+1);
		if(opt == 1)printf("%s %d %d %d\n","Add",rand()%n+1,rand()%n+1,rand()%100);		
		if(opt == 2)printf("%s %d %d %d\n","Cover",rand()%n+1,rand()%n+1,rand()%100);
		if(opt == 3)printf("%s %d %d\n","Change",rand()%(n-1)+1,rand()%100);
	}
	printf("%s","Stop");
	return 0;
}

标签:毛景树,Luogu,rson,tree,cover,int,add,P4315,lson
From: https://www.cnblogs.com/JT-dw/p/JT_NO-15.html

相关文章

  • 【LuoGu】3047 Nearby Cows G ——两次DFS+树上DP
    [USACO12FEB]NearbyCowsG题目描述给你一棵\(n\)个点的树,点带权,对于每个节点求出距离它不超过\(k\)的所有节点权值和\(m_i\)。输入格式第一行两个正整数\(n,k\)。接下来\(n-1\)行,每行两个正整数\(u,v\),表示\(u,v\)之间有一条边。最后\(n\)行,每行一个非负整数......
  • 【LuoGu】2014 选课——树上DP
    [CTSC1997]选课题目描述在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有\(N\)门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有......
  • luogu P1419 题解
    题目链接description给定一个长度为\(n\)的序列(值域为\([-10^4,10^4]\))和正整数\(st,ed\)。求一个区间,使得其长度\(\in[st,ed]\)且平均值最大,输出平均值。\(1\leqn\leq10^5\)solution这里给出一个复杂度线性的做法。求出前缀和数组\(s\),答案相当于\(\max\limit......
  • Luogu P5290 [十二省联考 2019] 春节十二响
    LuoguP5290[十二省联考2019]春节十二响题目链接题目大意一颗有根树,有点权,把点分成若干个集合,要求每个集合内不包含祖先关系,求集合的最大值的和的最小值.做题思路考虑合并两个集合,我们只需要不停把分别吧两个集合的最大值取出取max加入答案,再把大集合剩下的内......
  • 【LuoGu】1351 联合权值
    [NOIP2014提高组]联合权值题目描述无向连通图\(G\)有\(n\)个点,\(n-1\)条边。点从\(1\)到\(n\)依次编号,编号为\(i\)的点的权值为\(W_i\),每条边的长度均为\(1\)。图上两点\((u,v)\)的距离定义为\(u\)点到\(v\)点的最短距离。对于图\(G\)上的点对\((u,v......
  • 【题解】Luogu[P7706] 「Wdsr-2.7」文文的摄影布置
    Link一道很有意思的线段树题。第一步分析,我们要求最大的\(a_i+a_k-\min{(b_j)}\),事实上我们可以直接省去这个\(\min\)因为要最大化这个东西,选出来的\(b_j\)必然是最小的,所以原题转化为给定\(l,r\)求\(\max{(a_i-b_j+a_k)}\)其中\(i<j<k\)。第二步分析,我们发现这是一......
  • 【题解】Luogu-P2482 SDOI2010 猪国杀
    写了\(358\)行,\(11.94\mathrm{KB}\),有这么几个地方写挂了:反猪决斗一定选主猪。游戏结束判定是主猪死亡或全部反猪死亡。决斗可能被反杀,之后不能再出牌。点击查看代码#include<bits/stdc++.h>usingnamespacestd;intn,m;charCh[3];queue<char>Deck;in......
  • LuoguP7637 [BalticOI 2006 Day 1] BITWISE EXPRESSIONS
    题目大意给定\(N\)对数据,每对数据包含两个整数\(A_i\)和\(B_i\),表示这一对数据的\(v_i\)的范围:\(A_i\leqv_i\leqB_i\)。又将这\(N\)对数据分为\(P\)组,其中\(K_i\)表示第\(i\)组数据中有多少对数据。我们设第\(i\)组数据中将所有数按位与的结果为\(X_i\),求......
  • [刷题笔记] Luogu P2285 [HNOI2004] 打鼹鼠
    ProblemAnalysis我们初始可以任意决定机器人的位置,状态很多,暴力显然会寄掉。不妨先贪心的思考一下。我们肯定希望机器人初始在最先出现鼹鼠的洞,因为出现在没有鼹鼠的洞是无效的。题目保证输入数据是严格按照出现时间递增顺序给出。定义\(f_i\)表示前\(i\)只鼹鼠最多能打到......
  • [刷题笔记] Luogu P4933 大师
    ProblemDescription给定一个长度为\(n\)的数组\(h\),你可以从中选取若干数字,使得你选择的数组组成一个等差数列。特别地,单一的数字和只有两个数字也算作等差数列。求你可选择的方案数。答案对\(998244353\)取模。Analysis考虑\(f_{i,j}\)表示前\(i\)个数,公差为\(j\)......