【洛谷7516】[省选联考 2021 A/B 卷] 图函数(Floyd)

点此看题面

  • 对于一张有向图\(G\),定义函数\(f(u,G)\):从\(1\sim n\)枚举顶点\(v\),若\(u\)能到达\(v\)且\(v\)能到达\(u\),就将计数器\(cnt\)加\(1\)并删去顶点\(v\)及其连边,最终\(f(u,G)=cnt\)。并令\(h(G)=\sum_{u=1}^nf(u,G)\)。
  • 现给定一张\(n\)个点\(m\)条边的有向图\(G\),令\(G_i\)表示\(G\)删去前\(i\)条边后得到的图,求\(h(G)\)以及所有\(h(G_i)\)。
  • \(n\le10^3,m\le2\times10^5\)

一个重要结论

首先给出结论:\(f(u,G)\)就等价于有多少个顶点\(v\in[1,u]\),满足\(u\)能到达\(v\)且\(v\)能到达\(u\),且不经过任何编号小于\(v\)的点。

证明就考虑反证法。如果\(u\rightarrow v\rightarrow u\)的回路中存在一个编号比\(v\)小的点\(w\),那么\(w\)也必然能沿着这条回路满足\(u\)能到达\(w\)且\(w\)能到达\(u\),因此\(w\)肯定早就被删去,矛盾了。

这样一来我们就不要考虑对每个合法顶点\(v\)的删点操作,这样一来题意就清新了许多。

\(Floyd\)

要求从\(x\rightarrow y\rightarrow x\)只经过编号大于等于\(y\)的点,也就是说只能选择编号大于\(y\)的点作为转移点,因此就可以想到\(Floyd\)。

我们记录\(f_{x,y}\)表示\(x\rightarrow y\)路径上最早被删除边的最迟时间。

从大到小枚举转移点\(k\),此时编号大于\(k\)的点都已经转移过了,故我们可以在此时统计\(k\)的答案——枚举\(i\in(k,n]\),给\(ans[\min\{f_{i,k},f_{k,i}\}]\)加上\(1\)(这里的\(ans[i]\)相当于记录了最迟在第\(i\)条边删除前还能合法的点对数,只要最后做一遍后缀和就可以得出真正的答案了)。

接下来的转移就是\(Floyd\)基本操作了,枚举一对点\(i,j\)借助\(k\)转移。

具体实现中可能有点小卡常。。。

代码:\(O(n^3)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define M 200000
using namespace std;
int n,m,ans[M+5],f[N+5][N+5];
int main()
{
	RI i,j,k,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),f[x][y]=i;//初始化边权
	int *Fi,*Fk;for(k=n;k;--k)//从大到小枚举转移点
	{
		for(i=k+1;i<=n;++i) ++ans[min(f[i][k],f[k][i])];//统计当前转移点的答案
		for(i=1;i<=n;++i) if(x=f[i][k]) for(j=1,Fi=f[i]+1,Fk=f[k]+1;j<=n;++j,++Fi,++Fk) *Fi=max(*Fi,min(x,*Fk));//Floyd
	}
	for(ans[m+1]=n,i=m;i;--i) ans[i]+=ans[i+1];for(i=1;i<=m+1;++i) printf("%d ",ans[i]);return 0;//后缀和得到最终答案
}
上一篇:floyd算法:可以有负权边,但不能有负权回路


下一篇:Floyd