[POI2014]RAJ-Rally题解 拓扑+最长链

本文介绍了如何运用图论中的最长链问题解决算法,结合动态规划和多集合操作(如multiset)来实现边的插入、删除及求解最大值。文章通过一个具体的例子解释了在删除特定节点时如何更新最长链,并在所有操作完成后如何恢复图的状态。此方法适用于处理复杂图结构中的路径优化问题。
摘要由CSDN通过智能技术生成

[POI2014]RAJ-Rally

Grass Cownoiseur 很相似,
对于经过一条边 < u , v > <u,v> <u,v> 的最长链长度应该等于 f [ u ] + g [ v ] + 1 f[u]+g[v]+1 f[u]+g[v]+1
其中 f [ u ] f[u] f[u] 表示以 u u u 为终点的最长路即任意一点到 u u u 的最长路,
g [ v ] g[v] g[v] 表示以 v v v 为起点的最长路即 v v v 任意一点到 的最长路

A A A 为拓扑序小于当前枚举结点 i i i 的元素集合
B B B 为拓扑序大于当前枚举结点 i i i 的元素集合

初始所有点都在 B B B
在这里插入图片描述
若删去 i i i A A A 中的所有 f f f ,与 B B B 中的所有 g g g 都是不受影响的

在这里插入图片描述
如上图,若考虑删去 4 4 4 号点

应将 4 4 4 号点从 B B B 移出

< 3 , 4 >   < 1 , 4 >   < 4 , 5 > <3,4>\space <1,4> \space <4,5> <3,4> <1,4> <4,5> 这些边会受到影响

例如,删除 < 3 , 4 > <3,4> <3,4>,则应该删除一条长度为 f [ 3 ] + 1 + g [ 4 ] f[3]+1+g[4] f[3]+1+g[4] 的链
将所有受影响边删除

在所有受影响的边被删除后,我们需要得出其余边中的最长链

在这里插入图片描述
而在计算完当前点之后,我们应将所有受影响的被删除边再重新加回图中,并将当前节点放入 A A A

我们操作需要支持 插入、删除、求最大值

可使用 multisetmultiset 入门

做法:

  1. 先预处理出每个点的 f [ i ] , g [ i ] f[i],g[i] f[i],g[i]
  2. 将所有点的 g [ i ] g[i] g[i] 插入 multiset 中 (初始状态所有点都在集合 B 中)
  3. 按照拓扑序枚举所有点 i i i,将 g [ i ] g[i] g[i] 删除,再将受 i i i 影响的边删除,之后将 f [ u ] f[u] f[u] 插入 ,将所有受影响边重新插入
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
int n,m,ans=1e9+5,anspos;
int indeg1[MAXN],indeg2[MAXN];
int f[MAXN],g[MAXN],id[MAXN],cnt; 
vector <int> G1[MAXN],G2[MAXN];
queue <int> q;
void Topo1()
{
	for(int i=1;i<=n;i++)
		if(indeg1[i]==0)
		{
			q.push(i);
			id[++cnt]=i;
		}
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(auto v:G1[u])
		{
			indeg1[v]--;
			f[v]=max(f[v],f[u]+1);
			if(indeg1[v]==0) 
			{
				id[++cnt]=v;
				q.push(v);
			}
		}
	}
}
void Topo2()
{
	for(int i=1;i<=n;i++)
		if(indeg2[i]==0)  q.push(i);
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(auto v:G2[u])
		{
			indeg2[v]--;
			g[v]=max(g[v],g[u]+1);
			if(indeg2[v]==0) q.push(v);
		}
	}
}
multiset <int> s;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		G1[u].push_back(v); indeg1[v]++;
		G2[v].push_back(u); indeg2[u]++;
	}
	Topo1(); Topo2();
	for(int i=1;i<=n;i++) s.insert(g[i]);
	for(int i=1;i<=n;i++)
	{
		int u=id[i];
		s.erase(s.find(g[u]));
		for(auto v:G2[u]) // v-->u
			s.erase(s.find(f[v]+1+g[u]));
		if(*--s.end()<ans)
		{
			ans=*--s.end();
			anspos=u;
		}
		s.insert(f[u]);
		for(auto v:G1[u]) // u-->v
			s.insert(f[u]+1+g[v]);
	}
	cout<<anspos<<" "<<ans;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值