最怕你一生庸碌无为,却总是安慰自己平凡可贵。|

Mr_KaYa

园龄:2年7个月粉丝:2关注:2

AT_abc335_e

说句闲话

场上一开始还读错题,耽误好多时间。后来想到正解,但怎么写也写不对,吃了巨大罚时,菜死了。所以跑来写写题解水经验总结一下。

题意简述

原题传送门

给定一个 n 个点 m 条边的无向带权图,求 1n 点权单调不降路径上最多有多少不同的权值,输出最多不同权值数(1n,m2×105)。

题目解析

我一开始的朴素思路是直接 dp。具体地,设 fi 表示走到第 i 个点的最多不同权值数,则显然有转移方程 fv=max(fv,fu+1),其中 u,v 之间有边且 auav。但当你兴高采烈地写完就会发现 TLE。为什么呢?考虑这样以后每个点可能都会跑完整个图,复杂度平方级别,肯定不能过。

发现既然要走单调不降的路径,那么我们可以对每一条无向边定向,即小的连向大的。那么相等权值怎么办呢?考虑这些点不会多次影响答案,所以把这些点缩成一个点即可。这样以后满足偏序关系,所以新图是一个有向无环图(DAG),问题转化成最长链,直接在图上拓扑就行了。

还是有点细节的,具体见代码。我直接用 dfs 缩点了,懒得写 tarjan。

参考代码

马蜂不好看的话请各位大佬轻喷 qwq。

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define endl '\n'
#define eb emplace_back
#define N 200005
int n,m;
int a[N],scc[N],du[N],dis[N],cnt;
vector<int> g[N],e[N];
queue<int> q;
void dfs(int u){
	scc[u]=cnt;
	for(auto v:g[u]) if(!scc[v]&&a[u]==a[v]) dfs(v);
}
int main(){
	ios::sync_with_stdio(false);
    cin.tie(nullptr);
	memset(dis,-0x3f,sizeof(dis));
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=m;++i){
		int u,v;
		cin>>u>>v;
		if(a[u]<=a[v]) g[u].eb(v);
		if(a[v]<=a[u]) g[v].eb(u);
	}
	for(int i=1;i<=n;++i){
		if(!scc[i]){
			cnt++;
			dfs(i);
		}
	}
	for(int i=1;i<=n;++i){
		for(auto j:g[i]){
			if(scc[i]!=scc[j]){
				e[scc[i]].eb(scc[j]);
				du[scc[j]]++;
			}
		}
	}
	dis[scc[1]]=1;
	for(int i=1;i<=cnt;++i) if(!du[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto v:e[u]){
			if(!(--du[v])) q.push(v);
			dis[v]=max(dis[v],dis[u]+1);
		}
	}
	cout<<max(0,dis[scc[n]])<<endl;
	return 0;
}

完结撒花!(请管理通过,拜托啦。)

posted @   Mr_KaYa  阅读(13)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起