Codeforces 490F Treeland Tour(离散化 + 线段树合并)

题目链接 Treeland Tour

题目就是让你求树上LIS

先离散化,然后再线段树上操作。一些细节需要注意一下。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const int N = 200010;

int root[N];
int ls[N * 40], rs[N * 40], lis[N * 40], lds[N * 40];
int ncnt, n, ans;
int ret = 0;
vector <int> v[N];
int val[N];
int sx[N];
int icnt = 0;

void merge(int &x, int y){
	if (!x || !y){
		x = x + y;
		return;
	}

	lis[x] = max(lis[x], lis[y]);
	lds[x] = max(lds[x], lds[y]);
	ret = max(ret, max(lis[ls[x]] + lds[rs[y]], lds[rs[x]] + lis[ls[y]]));
	merge(ls[x], ls[y]);
	merge(rs[x], rs[y]);
}

void modify(int &x, int l, int r, int t, int v, int *a){
	if (!x) x = ++ncnt;
	a[x] = max(a[x], v);
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (t <= mid) modify(ls[x], l, mid, t, v, a);
	else modify(rs[x], mid + 1, r, t, v, a);
}

int query(int x, int l, int r, int ql, int qr, int *a){
	if (l > r) return 0;
	if (!x) return 0;
	if (ql <= l && r <= qr) return a[x];
	int ret = 0, mid = (l + r) >> 1;
	if (ql <= mid) ret = max(ret, query(ls[x], l, mid, ql, qr, a));
	if (qr >  mid) ret = max(ret, query(rs[x], mid + 1, r, ql, qr, a));
	return ret;
}

void dfs(int x, int fa){
	for (auto u : v[x]){
		if (u == fa) continue;
		dfs(u, x);
	}

	ret = 0;
	int nlis = 0, nlds = 0, ilis, ilds;
	for (auto u : v[x]){
		if (u == fa) continue;
		ilis = query(root[u], 1, icnt, 1, val[x] - 1, lis);
		ilds = query(root[u], 1, icnt, val[x] + 1, icnt, lds);
		merge(root[x], root[u]);
		ans = max(ans, ilis + nlds + 1);
		ans = max(ans, ilds + nlis + 1);
		nlis = max(nlis, ilis);
		nlds = max(nlds, ilds);
	}

	ans = max(ans, ret);
	modify(root[x], 1, icnt, val[x], nlis + 1, lis);
	modify(root[x], 1, icnt, val[x], nlds + 1, lds);
}

int main(){

	scanf("%d", &n);
	rep(i, 1, n){
		scanf("%d", val + i);
		sx[++icnt] = val[i];
	}

	sort(sx + 1, sx + icnt + 1);
	icnt = unique(sx + 1, sx + icnt + 1) - sx - 1;
	rep(i, 1, n) val[i] = lower_bound(sx + 1, sx + icnt + 1, val[i]) - sx;

	rep(i, 2, n){
		int x, y;
		scanf("%d%d", &x, &y);
		v[x].push_back(y);
		v[y].push_back(x);
	}

	dfs(1, 0);
	printf("%d\n", ans);
	return 0;
}
时间: 2024-12-13 18:41:00

Codeforces 490F Treeland Tour(离散化 + 线段树合并)的相关文章

Codeforces 490F Treeland Tour(dp)

题目链接:Codeforces 490F Treeland Tour 类似于nlogn的递增上升子序列算法. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int maxn = 6005; const int inf = 0x3f3f3f3f; int N, R[maxn], D[maxn], ans

CodeForces 600E Lomsat gelral(线段树合并)

题目链接:http://codeforces.com/problemset/problem/600/E You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour. Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the

Codeforces ECR47F Dominant Indices(线段树合并)

一个比较显然的做法:对每棵子树用线段树维护其中的深度,线段树合并即可. 本来想用这个题学一下dsu on tree,结果还是弃疗了. #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=

Codeforces 490F Treeland Tour 树上的最长上升子序列

题目链接:点击打开链接 题意: 给定n个点的树. 下面n个数表示点权. 下面n-1行给出树. 找一条链,然后找出这条链中的点权组成的最长上升子序列. 求:最长上升子序列的长度. 思路: 首先是维护一条链然后求答案,但是如果直接树形dp(记录每个点u,u往下递增和u往下递减的长度)会使序列是来回的,即递增和递减都在同一条链上. 枚举每个点作为子序列的开头,然后维护一条链进行LIS的nlogn做法. import java.io.PrintWriter; import java.util.Array

CodeForces 19D Points(离散化+线段树+单点更新)

题目链接: huangjing 题意:给了三种操作 1:add(x,y)将这个点加入二维坐标系 2:remove(x,y)将这个点从二维坐标系移除. 3:find(x,y)就是找到在(x,y)右上方的第一个点. 思路:我们可以建立n个set以x为横坐标,那么我们这个题就转化为找一个最小的x是否存在满足条件,那么x一旦被找到,那么纵坐标就自然而然的找到了,当然更新操作就是对maxy的维护,然后查询操作就是找出一个最小的x..还有因为n非常大,所以要采用离散化的方法,然后进行离线处理.还是就是掌握s

[CF490F]Treeland Tour(线段树合并)

树上LIS:树上找一条简单路径的子序列使点权严格单增,最大化长度. 原题数据过小,用线段树合并可以做到$O(n\log n)$. 每个点用一棵线段树维护以每个权值为结尾的LIS最长长度,线段树合并时更新子序列不包含当前点时的最大值,再线段树上区间询问得到包含时的最大值并更新线段树. 1 #include<cstdio> 2 #include<algorithm> 3 #define lson ls[x],L,mid 4 #define rson rs[x],mid+1,R 5 #d

codeforces Good bye 2016 E 线段树维护dp区间合并

题目大意:给你一个字符串,范围为'0'~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问,最少删除多少个字符,使得串中符合ugly串? 思路:定义dp(i, j),其中i=5,j=5,因为只需要删除2016当中其中一个即可,所以一共所需要删除的字符和需要的字符为20176,因此i和j只要5就够了. 然后转移就是dp(i,i) = 0, 如果说区间大小为1的话,那么如果是2017中的一个,那么就是dp(pos, pos+1) = 0, dp(pos,pos) =

[BZOJ3545] [ONTAK2010]Peaks(线段树合并 + 离散化)

传送门 由于困难值小于等于x这个很恶心,可以离线处理,将边权,和询问时的x排序. 每到一个询问的时候,将边权小于等于x的都合并起来再询问. .. 有重复元素的线段树合并的时间复杂度是nlog^2n #include <cstdio> #include <iostream> #include <algorithm> #define N 500001 int n, m, q, cnt, tot, size; int sum[N * 10], ls[N * 10], rs[N

Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)

题目链接  2016 ACM-ICPC EC-Final Problem G 题意  给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过w的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多. 次数相同时输出编号最小的那个颜色.强制在线. 求哪种颜色可以用线段树合并搞定. 关键是这个强制在线. 当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了. 在做最小生成树的时候每合并两个节点,另外开一个