题解:P13823 「Diligent-OI R2 C」所谓伊人

P13823 「Diligent-OI R2 C」所谓伊人

题意

给一个有向图,点有点权。

当一个点可达另一点时,两点可交换。

在点权最大前提下,最小化交换次数。

思路

先想点权什么时候最大,显然只要两点间有边相连,无论方向,这两点可交换。

此时,只要将原有向图改成无向图,就可以确定每个联通块内的点权最大值是一样的。

然而,怎样最小化交换次数呢?

1  ->  2  ->  3    1,3交换 ans=1

1  ->  2  <-  3    1,3交换 ans=2

上面 1 点到 3 点间方向改变了一次,答案的贡献就多一。因此要记录方向的改变次数。

如此一来,建一个双层图即可,一个正图,一个反图。

改变方向相当于从一个图走到另一个图。

实现

判联通块,\(O(n)\) 扫一遍全图。

算贡献时每个块跑一遍 SPFA(巨佬说这叫 01 BFS 蒟蒻不懂)。由于边权只有 \(0\)\(1\),基本退化成 BFS,\(O(n+m)\)

细节

每个块内源点(最大点)可能不止一个,本人用 vector 存。

特判自己与自己交换,贡献为 \(0\),所以跑完后答案改为 \(-1\)。(最后统一加了一)

CODE

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
int n,m;
int a[N];
vector<pair<int,int> > g[N];
int fa[N],tot;
int mx[N];
vector<int> p[N];
int dis[N];
void dfs(int rt){
	fa[rt]=tot;
	if(mx[tot]<=a[rt]&&rt<=n){
		if(mx[tot]<a[rt]){
			mx[tot]=a[rt];
			p[tot].clear();
			p[tot].push_back(rt);
		}
		else{
			p[tot].push_back(rt);
		}
	}
	for(int i=0;i<g[rt].size();i++){
		if(fa[g[rt][i].first])	continue;
		dfs(g[rt][i].first);
	}
	return;
}
queue<int> q;
void spfa(int rt){
	while(!q.empty())	q.pop();
	q.push(rt);
	q.push(rt+n);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<g[u].size();i++){
			int v=g[u][i].first;
			if(dis[u]+g[u][i].second<dis[v]){
				dis[v]=dis[u]+g[u][i].second;
				q.push(v);
			}
		}
	}
	dis[rt]=-1;
	dis[rt+n]=-1;
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	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;
		g[u].push_back(make_pair(v,0));
		g[v+n].push_back(make_pair(u+n,0));
	}
	for(int i=1;i<=n;i++){
		g[i].push_back(make_pair(i+n,1));
		g[i+n].push_back(make_pair(i,1));
	}
	for(int i=1;i<=n;i++){
		if(fa[i])	continue;
		tot++;
		dfs(i);
	}
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=tot;i++)
		for(int j=0;j<p[i].size();j++)
			dis[p[i][j]]=dis[p[i][j]+n]=0;
	for(int i=1;i<=tot;i++)
		for(int j=0;j<p[i].size();j++)
			spfa(p[i][j]);
	for(int i=1;i<=n;i++)
		cout<<min(dis[i],dis[i+n])+1<<' ';
	return 0;
}

题外话

出题人魔怔了,为了出题不惜放出头图!!!证据如下:

这里本来有一个 Klg 进行 mega dunk 的头图,但是出题人觉得过于【数据删除】于是撤掉了。

完结撒花!!!

posted @ 2026-01-30 10:11  concert_b  阅读(0)  评论(0)    收藏  举报