ABC335E题解

发布时间 2024-01-10 10:10:10作者: LiJoQiao

洛谷题面
感觉有点毒瘤,不过还是有些 trick 在的。

题意翻译(复制于洛谷题面):

给定一个 \(N\) 个点 \(M\) 条无向边的图,图上每个点都有其颜色。求所有经过点权单调不降的路径中,出现的不同颜色的个数最多是多少。

由于是单调不降的路径,所以可以点权大的点到点权小的点的路径对结果没有影响,可以当有向边连。
点权相同且相连的点可以来回走,对结果没有影响,可以缩成一个点。

这样这张无向图就变成了由点权小的点到点权大的点的 DAG。

建图的时候把所有边先存起来,等用并查集将所有点缩成一个的时候再连边。

然后我们可以在这张图上来计 \(1\) 到 \(N\) 这条路径上不同颜色的个数。

有的题解里用了 Dijkstra 等算法,由于 DAG 的性质所以求解的本质一样,不再赘述。

由于是 DAG,所以可以跑拓扑排序,但是直接 dp 是不对的,有可能变为了以其他点为起点到 \(N\) 的路径。

我们可以将其他点的 dp 值初始化为负无穷,然后 \(1\) 点的 dp 值为 \(1\),此时进行转移要么是 \(1\) 到 \(N\) 路径的值,要么是负无穷,进行一次判断即可。

代码如下。

#include<bits/stdc++.h>
using namespace std;
template<typename T>
T read(){
    T f=1,x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return f*x;
}
constexpr int MAXN=2e5+10,MAXM=2e5+10;
int n,m,a[MAXN],fa[MAXN],cnt,head[MAXN],tcnt,dp[MAXN],in[MAXN];
bool vis[MAXN];
struct EDGE1{
    int v,nxt;
}edge[MAXM];
struct EDGE2{
    int u,v;
}tedge[MAXM];
void add(int u,int v){
    edge[++cnt]={v,head[u]};
    head[u]=cnt;
    ++in[v];
}
namespace sol{
    int sget(int x){
        if(fa[x]==x)return x;
        else return fa[x]=sget(fa[x]);
    }
    void merge(int x,int y){
        fa[sget(y)]=sget(x);
    }
    void solve(){
        n=read<int>();m=read<int>();
        for(int i=1;i<=n;++i){
            a[i]=read<int>();
            fa[i]=i;
        }
        for(int i=1,u,v;i<=m;++i){
            u=read<int>();v=read<int>();
            if(a[u]==a[v])merge(u,v);
            else{
                if(a[u]>a[v])swap(u,v);
                //a[u]<a[v] 
                tedge[++tcnt]={u,v};
            }
        }
        for(int i=1;i<=tcnt;++i){
            add(sget(tedge[i].u),sget(tedge[i].v));
        }
        queue<int>q;
        for(int i=1;i<=n;++i){
        	if(!in[sget(i)]){
        		in[sget(i)]=1;
        		q.push(sget(i));
			}
		}
		memset(dp,0x80,sizeof(dp));
		dp[sget(1)]=1;
        while(!q.empty()){
        	int u=q.front();q.pop(); 
            for(int i=head[u];i;i=edge[i].nxt){
            	int v=edge[i].v;
            	dp[v]=max(dp[u]+1,dp[v]);
            	--in[v];
            	if(in[v]==0){
            		q.push(v);
				}
			}
        }
        printf("%d\n",max(dp[sget(n)],0));
    }
}
int main(){
    sol::solve();
    return 0;
}