题意简述
给定一个 n n n 个点 m m m 条边的无向图,每个点有一个点权 a i a_i ai。对于每条从 1 1 1 到 n n n 的简单路径,路径的美丽值定义如下:
∙ \bullet ∙ 令序列 s s s 为按顺序走过的点的点权构成的序列。
∙ \bullet ∙ 若 s s s 不是单调不下降序列,该路径美丽值为 0 0 0。否则,该路径美丽值为 s s s 中不同元素的个数。
输出 1 1 1 到 n n n 的简单路径中美丽值最大为多少。
解题思路
首先按照点权排序。排序完之后,对于第 i i i 个点,我们只需要从它前面找小于它的点转移。但是我们不太好处理点权相同的点。如何解决?当点权为 x x x 的点全部转移完之后,再 dfs 跑一遍点权为 x x x 的点,取其中的最大值进行转移即可。总的时间复杂度应该是 Θ ( n ) \Theta(n) Θ(n),但是常数较大。
代码示例
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,dp[200010],num=0,vis[200010]={0},b[200010];
struct node{
int u,num;
bool operator<(const node &b)const{
return num<b.num;
}
}a[200010];
vector<int> G[200010];
set<int> s;
void dfs(int x){
s.insert(x);
//将遍历的点存进来
num=max(num,dp[x]);
//取最大值
vis[x]=1;
for(int v:G[x]) if(!vis[v]&&b[v]==b[x]) dfs(v);
//注意,只扫和当前点点权相同的点
}
signed main(){
memset(dp,-0x3f,sizeof(dp));
//初始化不能为0,否则转移会出问题
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i].num,a[i].u=i,b[i]=a[i].num;
//b数组存储了每个点的点权
//因为a数组排序后会打乱,所以b数组方便后续使用
sort(a+1,a+n+1);
//按点权排序
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dp[1]=1;
//1号点出发
for(int i=1;i<=n;i++){
for(int v:G[a[i].u]){
if(a[i].num>b[v]) dp[a[i].u]=max(dp[a[i].u],dp[v]+1);
if(a[i].num==b[v]) dp[a[i].u]=max(dp[a[i].u],dp[v]);
}
//扫一遍跟它相邻的点,进行更新
if(a[i].num!=a[i+1].num||i==n){
//如果当前点的点权和下一个点不同,说明点权和当前点点权相同的点全部更新完毕
//可以进行一次总转移了
int j=i;
while(a[j].num==a[i].num&&j>=1){
//扫所有的点权和当前点相同的点
if(!vis[a[j].u]){
s.clear();
num=-0x3f3f3f3f3f3f3f3f;
dfs(a[j].u);
//找其中的最大值
for(int v:s) dp[v]=num;
//赋值
}
j--;
}
}
}
cout<<(dp[n]<0?0:dp[n])<<endl;
return 0;
}