【CF】1900E-Transitive Graph 题解

传送门:1900E
标签:图论

题目大意

你有一个带有 n n n 个顶点和 m m m 条边的有向图 G G G。最初,图 H H H 与图 G G G 相同。然后你决定执行以下操作:如果存在图 H H H 中的顶点三元组 a , b , c a, b, c a,b,c,使得有一条从 a a a b b b 的边和一条从 b b b c c c 的边,但没有从 a a a c c c 的边,则添加一条从 a a a c c c 的边。
反复执行以上步骤,直到不存在这样的三元组为止。注意,执行这些操作后, H H H 中的边数最多可达 n 2 n^2 n2。你还给图 H H H 的顶点写了一些值。具体来说,第 i i i 个顶点上有值 a i a_i ai 写在上面。考虑由 k k k 个不同的顶点组成的简单路径,索引分别为 v 1 , v 2 , … , v k v_1, v_2, \ldots, v_k v1,v2,,vk。该路径的长度为 k k k。该路径的价值定义为 ∑ i = 1 k a v i \sum_{i=1}^k a_{v_i} i=1kavi。一个简单路径被认为是最大的,如果图中没有其他具有更大长度的简单路径。在 H H H 中的所有最长大于或等于 k k k 的简单路径中,找到价值最小的那个。

输入:每个测试包含多个测试用例。第一行包含整数 t t t 1 ≤ t ≤ 1 0 4 1≤t≤10^4 1t104),表示测试用例的数量。接下来是测试用例的描述。每个测试用例的第一行包含两个整数 n n n m m m 1 ≤ n , m ≤ 2 ⋅ 1 0 5 1≤n,m≤2⋅10^5 1n,m2105),表示顶点数和边数。第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 0 ≤ a i ≤ 1 0 9 0≤a_i≤10^9 0ai109),表示写在图 H H H 上的顶点值。接下来的 m m m 行中的每行包含两个整数 v i v_i vi u i u_i ui 1 ≤ v i , u i ≤ n 1≤v_i,u_i≤n 1vi,uin),表示图 G G G 中从顶点 v i v_i vi 到顶点 u i u_i ui 的边。注意,边是有方向的。还要注意,图可能有自环和多条边。保证了所有测试用例的 n n n m m m 总和不超过 2 ⋅ 1 0 5 2⋅10^5 2105

输出:对于每个测试用例,输出两个数字 — H H H 中最长大于或等于 k k k 的简单路径的长度及其最小可能价值。

算法分析

主要观察点在于 H H H 的样子。 G G G 中的所有强连通分量 (SCC) 在 H H H 中将成为完全图。其次,取任何两个顶点 a a a b b b,它们不在同一个 SCC 中。我们可以让 S a S_a Sa 是与 a a a 同在一个 SCC 的顶点集(包括 a a a)。同样, S b S_b Sb 是与 b b b 同在一个 SCC 的顶点集。如果有从 a a a b b b 的边,那么对于任意属于 S a S_a Sa 的顶点 x x x 和属于 S b S_b Sb 的顶点 y y y,都有从 x x x y y y 的边。关于图的这两个事实都可以通过归纳证明。
现在,假设存在一条经过至少一个 SCC 的最长路径。那么这条路径将经过所有顶点,因为 SCC 中的所有顶点都连接到相同的外部顶点,并且因为 SCC 是一个完整的子图。
现在我们可以构造 H ′ H' H 图。 H H H 中的每个 SCC 都将是 H ′ H' H 中的一个顶点。顶点上的数字等于构成该 SCC 的顶点之和。如果它们之间的原始 SCC 之间有边,则会在两个新顶点之间添加边。边的权重等于进入的 SCC 的大小。还会添加一个位于索引 0 的额外顶点,并在其与其他所有顶点之间创建边。权重将根据进入的顶点的 SCC 大小确定。
由于之前的观察, H ′ H' H 的答案与 H H H 相同。但是请注意, H ′ H' H 是 DAG。这意味着可以使用 DP 计算其答案。
总时间和内存复杂度为 O ( n ) O(n) O(n)

代码实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int val[N];
vector<int> a[N],g[N];
int dfn[N],low[N],cnt;
bool vis[N];
stack<int> st;
int sum[N],siz[N],tot;
int bl[N];
void del()
{
	siz[tot]++;
	vis[st.top()]=0;
	sum[tot]+=val[st.top()];
	bl[st.top()]=tot;
	st.pop();
}
void Tarjan(int x)
{
	dfn[x]=low[x]=++cnt;
	vis[x]=1;
	st.push(x);
	for(int to:a[x])
	{
		if(!dfn[to])
		{
			Tarjan(to);
			low[x]=min(low[x],low[to]);
		}
		else if(vis[to])
			low[x]=min(low[x],dfn[to]);
	}
	if(low[x]==dfn[x])
	{
		tot++;
		while(st.top()!=x)
			del();
		del();
	}
}
int in[N];
int len[N],f[N];
int t;
void run(int o)
{
	int n,m;
	cin>>n>>m;
	tot=cnt=0;
	for(int i=1;i<=n;i++)
	{
		dfn[i]=low[i]=in[i]=len[i]=f[i]=val[i]=vis[i]=sum[i]=siz[i]=bl[i]=0;
		a[i].clear(),g[i].clear();
	}
	for(int i=1;i<=n;i++)
		cin>>val[i];
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		a[x].push_back(y);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			Tarjan(i); 
	for(int i=1;i<=n;i++)
		for(int to:a[i])
			if(bl[to]!=bl[i])
			{
				g[bl[to]].push_back(bl[i]);
				in[bl[i]]++;
			}
	queue<int> q;
	for(int i=1;i<=tot;i++)
		if(!in[i])
			q.push(i);
	int ans1=0,ans2=0;
	while(q.size())
	{
		int x=q.front();
		q.pop();
		len[x]+=siz[x];
		f[x]+=sum[x];
		if(ans1<len[x])
			ans1=len[x],ans2=f[x];
		else if(ans1==len[x]&&ans2>f[x])
			ans2=f[x];
		for(int to:g[x])
		{
			in[to]--;
			if(len[to]<len[x])
				len[to]=len[x],f[to]=f[x];
			else if(len[to]==len[x]&&f[to]>f[x])
				f[to]=f[x];
			if(!in[to])
				q.push(to);
		}
	}
	cout<<ans1<<' '<<ans2<<endl;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>t;
	for(int i=1;i<=t;i++)
		run(i); 
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值