AT_abc335_e
说句闲话
场上一开始还读错题,耽误好多时间。后来想到正解,但怎么写也写不对,吃了巨大罚时,菜死了。所以跑来写写题解水经验总结一下。
题意简述
给定一个 \(n\) 个点 \(m\) 条边的无向带权图,求 \(1\) 到 \(n\) 点权单调不降路径上最多有多少不同的权值,输出最多不同权值数(\(1\le n,m\le 2\times 10^5\))。
题目解析
我一开始的朴素思路是直接 dp。具体地,设 \(f_i\) 表示走到第 \(i\) 个点的最多不同权值数,则显然有转移方程 \(f_v=\max(f_v,f_u+1)\),其中 \(u,v\) 之间有边且 \(a_u\le a_v\)。但当你兴高采烈地写完就会发现 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;
}
完结撒花!(请管理通过,拜托啦。)

浙公网安备 33010602011771号