首页 > 其他分享 >Luogu P4577 [FJOI2018] 领导集团问题

Luogu P4577 [FJOI2018] 领导集团问题

时间:2023-06-09 11:56:22浏览次数:41  
标签:lazy P4577 int Luogu tree FJOI2018 max id dp

[FJOI2018] 领导集团问题

题目描述

一个公司的组织领导架构可以用一棵领导树来表示。公司的每个成员对应于树中一个结点 \(v_i\),且每个成员都有响应的级别 \(w_i\)。越高层的领导,其级别值 \(w_i\) 越小。树中任何两个结点之间有边相连,则表示与结点相应的两个成员属于同一部门。领导集团问题就是根据公司的领导树确定公司的最大部门。换句话说,也就是在领导树中寻找最大的部门结点子集,使得的结点 \(v_i\) 和 \(v_j\),如果 \(v_i\) 是 \(v_j\) 的子孙结点,则 \(w_i \ge w_j\)。

编程任务:对于任意对于给定的领导树,计算出领导树中最大的部门结点子集。

输入格式

第一行有一个正整数 \(n\),表示领导树的结点数。

接下来的一行中有 \(n\) 个整数。第 \(i\) 个数表示 \(w_i\)。

再接下来的 \(n-1\) 行中,第 \(i\) 行有一个整数 \(v_i\) 表示 \(v_i\) 是 \(i+1\) 的双亲结点。

输出格式

输出找到的最大的部门的成员数。

样例 #1

样例输入 #1

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

样例输出 #1

4

提示

对于 \(10\%\) 的数据,\(n\le 20\);

对于 \(40\%\) 的数据,\(n\le 2000\);

对于 \(100\%\) 的数据,\(1\le n\le 2\times 10 ^ 5\),\(0 < w_i \le 10 ^ 9\)。

/*dp式子
  dp[i][j]表示在第i个点,以j为根节点的小根堆的最大个数
  dp[i][v] = sigma(max(dp[son][v->n]));sigma中必须要有一个dp[son][v]
  dp[i][w[i]] = max(dp[i][w[i]], sigma(max(dp[son][w[i]->n])) + 1);
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define lid (tree[id].lson)
#define rid (tree[id].rson)
const int maxN = 2e5 + 520.1314;
using namespace std;

int n, tot;
int w[maxN];
int cnt, head[maxN], nex[maxN], to[maxN];
int root[maxN], res[maxN];

struct Segment_Tree {
	int lson, rson;
	int dp, lazy;
}tree[maxN * 500];

struct MergeSegmentTree {
	inline int NewNode() {
		int id = ++ tot;
		tree[id].lson = tree[id].rson = 0;
		tree[id].lazy = tree[id].dp = 0;
		return id;
		//建立新节点
	} 
	inline void PushDown(int id) {
		if (tree[id].lazy != 0) {
			int temp = tree[id].lazy;
			if (lid != 0) {
				tree[lid].dp = tree[lid].dp + temp;
				tree[lid].lazy = tree[lid].lazy + temp;
			}
			if (rid != 0) {
				tree[rid].dp = tree[rid].dp + temp;
				tree[rid].lazy = tree[rid].lazy + temp;
			}
			tree[id].lazy = 0;
			//若下放时没有lid或rid 那么id的lazy就不会对其造成影响
			//故可以直接删除 
		}
		return;
	}
	inline void PushUp(int id) {
		tree[id].dp = max(tree[lid].dp, tree[rid].dp);
		return;
	}
	void Update(int &id, int l, int r, int pos, int num) {
		if (id == 0) id = NewNode();
		if (l == r) {
			tree[id].dp = max(tree[id].dp, num);
			//找最大值
			return;
		}
		PushDown(id);
		int mid = (l + r) >> 1;
		if (pos <= mid) {
			Update(lid, l, mid, pos, num);
		}
		else {
			Update(rid, mid + 1, r, pos, num);
		}
		PushUp(id);
		return;
	}
	int Query(int id, int l, int r, int vl, int vr) {
		int ans = 0;
		if (id == 0) return 0;
		if (vl <= l && r <= vr) {
			return tree[id].dp;
		}
		PushDown(id);
		int mid = (l + r) >> 1;
		if (l <= mid) ans = max(ans, Query(lid, l, mid, vl, vr));
		if (r > mid) ans = max(ans, Query(rid, mid + 1, r, vl, vr));
		return ans;
	}//单点查询
	int Merge(int &a, int &b, int l, int r, int &amax, int &bmax) {
		if (a == 0 && b == 0) return 0;
		if (a == 0 && b != 0) {
			bmax = max(bmax, tree[b].dp);
			tree[b].dp += amax;
			tree[b].lazy += amax;
			return b;
			//对应sigma(max(dp[son][v->n]))
			//tree[b].dp += amax;在这里代表max(dp[b][l->r])加上max(dp[a][r->n])
			//tree[b].lazy += amax;代表在l->r的区间内其他有dp值但不是max(dp[b][l->r])的地方加max(dp[a][r->n])
		}
		else if (a != 0 && b == 0) {
			amax = max(amax, tree[a].dp);
			tree[a].dp += bmax;
			tree[a].lazy += bmax;
			return a;
			//同上
		}
		else if (l == r) {
			amax = max(amax, tree[a].dp);
			bmax = max(bmax, tree[b].dp);
			tree[a].dp = max(tree[a].dp + bmax, amax + tree[b].dp);
			//对应sigma中必须要有一个dp[son][v]
			return a;
		}
		PushDown(a);
		PushDown(b);//都要PushDown
		int mid = (l + r) >> 1;
		tree[a].rson = Merge(tree[a].rson, tree[b].rson, mid + 1, r, amax, bmax);//注意先处理后面的dp
		tree[a].lson = Merge(tree[a].lson, tree[b].lson, l, mid, amax, bmax);
		PushUp(a);
		return a;
	}
}t;

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 x * f;
}

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

void DFS(int now) {
	int sum = 0;
	bool flag = true;
	for (int i = head[now]; i != 0; i = nex[i]) {
		DFS(to[i]);
		sum += t.Query(root[to[i]], 1, n, w[now], n);
	}
	for (int i = head[now]; i != 0; i = nex[i]) {
		if (flag == true) {
			root[now] = root[to[i]];//父亲节点刚开始时的线段树时是空的
			flag = false;
		}
		else {
			int lmax = 0, rmax = 0;
			root[now] = t.Merge(root[now], root[to[i]], 1, n, lmax, rmax);
			//将儿子的线段树合并到父亲来
		}
	}
	t.Update(root[now], 1, n, w[now], sum + 1);
	//在合并后的比较dp[i][w[i]] 与 sigma(max(dp[son][w[i]->n])) + 1的大小并更新;
	return;
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++ i) {
		w[i] = read();
		res[i] = w[i];
	}
	sort(res + 1, res + 1 + n);
	int len = unique(res + 1, res + 1 + n) - (res + 1);
	for (int i = 1; i <= n; ++ i) {
		w[i] = lower_bound(res + 1, res + 1 + len, w[i]) - res;
	}//离散化
	for (int i = 1, temp; i <= n - 1; ++ i) {
		temp = read();
		AddPoi(temp, i + 1);
	}
	DFS(1);
	printf("%d\n", t.Query(root[1], 1, n, 1, n));
	//输出答案
	return 0;
}

标签:lazy,P4577,int,Luogu,tree,FJOI2018,max,id,dp
From: https://www.cnblogs.com/jueqingfeng/p/17468807.html

相关文章

  • Luogu P5446 [THUPC2018] 绿绿和串串
    根据题目能发现一个性质,设转化前的字符串为\(s\),转化后的字符串为\(s'\),则\(s'_{|s|}\)为\(s'\)的中心,即\(s'_{|s|}\)求出来的回文串左边界为\(1\)右边界为\(|s'|\)找出这个性质就挺简单了,考虑先对给出的\(S\)用\(\text{manacher}\)求出每个节点\(i\)对应的左边......
  • Luogu P3224 [HNOI2012]永无乡
    [HNOI2012]永无乡题目描述永无乡包含\(n\)座岛,编号从\(1\)到\(n\),每座岛都有自己的独一无二的重要度,按照重要度可以将这\(n\)座岛排名,名次用\(1\)到\(n\)来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛\(a\)出发经过若干座(含\(0\)......
  • [刷题笔记] Luogu P3073 [USACO13FEB]Tractor S
    ProblemSolution和汽车拉力比赛差不多,思路都是二分,二分\(d\),但是汽车拉力比赛从一个路标开始搜即可,本题没有给定起点。一条合法路径起点是未知的,不得随便从一个点开始搜,否则可能找不到正确路径。怎么处理呢?容易想到对于每一个二分的\(d\),开一个\(n^2\)的循环,从每一个点开始搜......
  • Luogu P3605 [USACO17JAN]Promotion Counting P
    [USACO17JAN]PromotionCountingP题目描述Thecowshaveonceagaintriedtoformastartupcompany,failingtorememberfrompastexperiencethatcowsmaketerriblemanagers!Thecows,convenientlynumbered\(1\ldotsN\)(\(1\leqN\leq100,000\)),o......
  • Luogu P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    [Vani有约会]雨天的尾巴/【模板】线段树合并题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无奈......
  • 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......