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

点此看题面

  • 对于一张有向图(G),定义函数(f(u,G)):从(1sim 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))
  • (nle10^3,mle2 imes10^5)

一个重要结论

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

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

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

(Floyd)

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

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

从大到小枚举转移点(k),此时编号大于(k)的点都已经转移过了,故我们可以在此时统计(k)的答案——枚举(iin(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;//后缀和得到最终答案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7516.html