• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

llwwll

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

Tarjan问题

强连通

学习资料

强连通,又名\(scc\),即有向图中可以相互到达的子图,如 \(\quad\) 3->4;4->3 3与4即一对\(scc\);
\(Tarjan\)的作用可以将有环图转为有向无环图
既然是有向无环图 拓扑序的优势就体现出来了
更新树中的节点就可以通过拓扑排序进行

最优贸易

题意分析

在一个有向有环图中,求出从\(1\)开始的到达\(n\)的所有路径中,最大的点权差

这时候的数据结构狂魔就拿出了\(Tarjan\),重新建图为\(DAG\),用\(BFS\)拓扑\(dp\)一下
就\(A\)了这题

点击查看代码
#include<bits/stdc++.h>
#define maxn 100010
#define N 500010
#define int long long
using namespace std;
int head[N],nex[N],ver[N],tot;
int dfs[maxn],low[maxn],vis[maxn],dp[maxn],Gro[maxn],st[maxn];
int cnt,top,group,MaxG[maxn],MinG[maxn],in[maxn];
int n,m,val[maxn];
void add(int u,int v)
{
	ver[++tot]=v; nex[tot]=head[u];head[u]=tot;
}

int head2[N],nex2[N],ver2[N],tot2,pre_min[N];
void add2(int u,int v)
{
	ver2[++tot2]=v;  nex2[tot2]=head2[u];  head2[u]=tot2;
}

void tarjan(int x)
{
	dfs[x]=low[x]=++cnt;
	st[++top]=x; vis[x]=1;
	for(int i=head[x];i;i=nex[i])
	{
		int v=ver[i];
		if(!dfs[v])
		{;
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else
		if(vis[v])
		{
			low[x]=min(low[x],dfs[v]);
		}
	}
	if(dfs[x]==low[x])
	{
		int y; group++;
		do
		{
			y=st[top]; top--;
			Gro[y]=group; vis[y]=0;
			
			MaxG[group]=max(MaxG[group],val[y]);
			MinG[group]=min(MinG[group],val[y]);
			
		}while(x!=y);
	}
}
queue<int> q;
void BFS(int x)
{
	pre_min[x]=min(pre_min[x],MinG[x]);
	dp[x]=max(dp[x],MaxG[x]-pre_min[x]);
	while(!q.empty())
	{
		int x=q.front(); q.pop();
		for(int i=head2[x];i;i=nex2[i])
		{
			int v=ver2[i];
			in[v]--;
			pre_min[v]=min(pre_min[x],MinG[v]);
			dp[v]=max(dp[x],max(dp[v],MaxG[v]-pre_min[v]));
			if(!in[v])
			{
				q.push(v);
			}
		}
	}
}

signed main()
{
	scanf("%lld%lld",&n,&m);
	
	for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
	
	for(int i=1,u,v,c;i<=m;i++)
	{
		scanf("%lld%lld%lld",&u,&v,&c);
		if(c==1) add(u,v);
		if(c==2) add(u,v),add(v,u);
	}
	
	memset(MinG,100,sizeof MinG);
	for(int i=1;i<=n;i++) if(!dfs[i]) tarjan(i);

	
//	for(int i=1;i<=n;i++) cout<<Gro[i]<<" ";
	
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j;j=nex[j])
		{
			
			int v=ver[j];
			if(Gro[v]!=Gro[i]) 
			{
//				cout<<i<<" = "<<Gro[i]<<"----"<<v<<" = "<<Gro[v]<<endl;
				add2(Gro[i],Gro[v]);
				in[Gro[v]]++;
			}
		}
	}
	for(int i=1;i<=group;i++) 
	{
//		cout<<i<<" "<<in[i]<<endl;;
		if(!in[i])
		{
//			cout<<"!!  "<<i<<endl;
			q.push(i);
		}
	}
	
 	memset(pre_min,127,sizeof pre_min);
	BFS(Gro[1]);
	cout<<dp[Gro[n]];
	return 0;
}
/*

5 5
4 3 5 6 1 
1 2 1 
1 4 1 
2 3 2 
3 5 1
4 5 2 

12 14
100 10 11 10 10 1 1 1 1 1 1 1
1 4 1
4 5 2
1 2 1
2 3 2
5 6 1
6 7 2
7 12 1
3 8 1
2 5 1
8 9 2
3 10 1
10 11 2
11 12 1
9 12 1

*/

但是 这题其实仅用简简单单的\(DFS\)就可以解决\(\quad\) 再次感谢巨佬@HY喵的出其不意的思路
在每次\(DFS\)时,用一个\(Flag\)判断是否可以更新

点击查看代码
void dfs(int x,int minx,int pre) //x表示当前访问的节点编号,minx表示目前为止的最小的商品价格,pre表示上一个节点的编号
{                       
    int flag=1;                                          //用于判断是否已被访问过,需要终止
    minx=min(c[x],minx);                                 //判断本次的商品价格是否是最小值
    if (mi[x]>minx) mi[x]=minx,flag=0;                   //判断mi数组(记录了每次的最小值)并更新,在更新的同时把flag设为0(不用退出了)
    做动态规划;                                         //别着急flag还会继续更新的
    if (flag) return;                                    //退出,千万不能露,不然程序就会放飞自我,堆栈溢出->完美MLE只有10分。
    for (int i=0;i<g[x].size();i++) dfs(g[x][i],minx,x); //继续进行递归调用,访问目前节点能访问到的节点。
}

posted on 2022-09-24 08:17  llwwll  阅读(28)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3