codeforces960F 2000分动态开点线段树

题目传送门

题意:

现在有n条带有权值的有向边,每条边表示从节点u到节点v的权值是w,第i条输入的边的编号是i。

这个图可能有自环,有重边,不保证连通。

现在让你找一条最长的路径,路径的长度是路径上有向边的个数。

按照这个有向路径的方向,每条路径上的边,编号严格递增,权值严格递增。

输出这个路径的长度。

题解:

S是一个有向边,则定义len(S)是以S为路径的初始边的最长路径长度。

这道题我是倒着做的,就是从编号大的到编号小的枚举。

对于当前边S,是通过满足下列条件的边T更新的。

(1)T的编号大于S的编号。

(2)T的起点是S的终点。即S可以直接到达T。

(3)T的权值大于S的权值。

找到以T为初始边的最长路径,可以去更新以S为初始边的最长路径长度。

即len(S) = max{len(T)} + 1。

最大的len(S)即为答案。

下面分析在当前情景下查找区间最大值的方法:

首先对于每个编号开一棵线段树,因为每个编号都有区间查询最大值的需求。

假设当前编号是x,那么开第x棵线段树时,线段树叶子节点的下标是权值,叶子节点存储的信息是路径长度。

我没有每棵线段树都开一个根节点,而是把每棵线段树维护的区间映射到一个大区间的一部分了,我觉得这样好维护。

感受:

这道题看了之后发现需要开1e5个线段树去维护,直接就爆空间了。

然后又不得不去学鸽了很久并且以为很难的动态开点线段树。

看了动态开点线段树博客后,秒懂,然后1A。(不过我的202ms真的比别人的都慢啊qwq)

原来是我看到它的名字把他想难了,其实和字典树的开点方式一模一样。

很久没有不看题解1A了,真的好爽。

题解:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
const int maxn = 1e5 + 5 ;
const int maxm = 5e6 + 5 ;
int n , m ;
struct Edge
{
	int u , v , w ;
} edge[maxn] ;
int root , cnt , ls[maxm] , rs[maxm] , max1[maxm] ;
void update(int &id , ll l , ll r , ll x , int y)
{
	if(l > r)  return ;
	if(!id)  id = ++ cnt ;
	ll mid = (l + r) / 2 ;
	if(l == r)
	{
		max1[id] = max(max1[id] , y) ;
		return ;
	}
	if(x <= mid)
	  update(ls[id] , l , mid , x , y) ;
	else
	  update(rs[id] , mid + 1 , r , x , y) ;
	max1[id] = max(max1[ls[id]] , max1[rs[id]]) ;
}
int query(int id , ll l , ll r , ll x , ll y)
{
	if(x > y)  return 0 ;
	if(!id) return 0 ;
	int ans = 0 ;
	ll mid = (l + r) / 2 ;
	if(x <= l && r <= y)
	  return max1[id] ;
	if(x <= mid) ans = max(ans , query(ls[id] , l , mid , x , y)) ;
	if(y > mid)  ans = max(ans , query(rs[id] , mid + 1 , r , x , y)) ;
	return ans ;
}
int main()
{
	int ans = 0 ;
	ll len = 1e5 + 1 ;
	scanf("%d%d" , &n , &m) ;
	for(int i = 1 ; i <= m ; i ++)  
	  scanf("%d%d%d" , &edge[i].u , &edge[i].v , &edge[i].w) , edge[i].w ++ ;
	for(int i = m ; i >= 1 ; i --)
	{
		int u = edge[i].u , v = edge[i].v , w = edge[i].w ;
		ll l = len * (v - 1) + w + 1 , r = len * (v - 1) + len ;
		ll c = len * (u - 1) + w ;
		ll x = query(root , 1 , len * len , l , r) ;
		int y = x + 1 ;
		update(root , 1 , len * len , c , y) ;
		ans = max(ans , y) ;
	}
	printf("%d\n" , ans) ;
	return 0 ;
} 
发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104082964
今日推荐