P7516-[省选联考2021A/B卷]图函数【bfs】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P7516


1|1题目大意

懒了,直接抄题意了
对于一张 n 个点 m 条边的有向图 G(顶点从 1n 编号),定义函数 f(u,G)

  1. 初始化返回值 cnt=0,图 G=G
  2. 1n 按顺序枚举顶点 v,如果当前的图 G; 中,从 uv 与从 vu 的路径都存在,则将 cnt+1,并在图 G; 中删去顶点 v 以及与它相关的边。
  3. 2 步结束后,返回值 cnt 即为函数值。

现在给定一张有向图 G,请你求出 h(G)=f(1,G)+f(2,G)++f(n,G) 的值。

更进一步地,记删除(按输入顺序给出的)第 1i 条边后的图为 Gi1im),请你求出所有 h(Gi) 的值。

1n103,1m2×105


1|2解题思路

但凡一个不按惯性思维思考的方法都可以做出这道题
在这里插入图片描述

这个删边就很意义不明,反过来直接改成加边就简单很多。

然后还有一个问题就是是否删点的判断也是没有必要的:

假设对于起点u能走到vv不能走到u,那么显然并不存在一个节点x使得u能走到xx能走到u并且v是这些路径的必经点,因为那么v肯定在这个环上,那么v显然能走到u

所以现在f(u,G)能否统计v就变为了判断是否存在一个uvu的环使得路径上的所有点编号不小于min(u,v)

那么不妨考虑一下两个点对(u,v)之间不断加边之后第一次产生贡献的时间,每条边的权值设为加入的时间,这个时间就是uvu的一条不经过编号小于min(u,v)点的情况下最大权值最小的路径。

这样Flody就有O(n3)的算法了。

然后我们想了很久感觉最短路行不通,那么此时就需要摒弃惯性思维的想法了,我们考虑另一个更暴力的做法,我们每次加边然后暴力判断每个点之间的联通,发现这样的时间复杂度是O(nm2)的。

然后依旧的我们考虑另一种可能,我们最外层不枚举加边,而是枚举需要统计答案的起点u,然后每次加一条边(x,y),如果u能走到x且不能走到y那么此时u能走到y了,从y开始bfs所有其他没有走过的节点,需要注意的是走过的边两边都是遍历过的所以可以直接删除。

这样的时间复杂度就是O(n(n+m)),可以通过本题。

需要卡常


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N=1e3+10,M=2e5+10; struct node{ int to,next; }a[M]; int n,m,f[N][N],g[N][N]; int ex[M],ey[M],ans[M]; vector<int> G[N],D[N];queue<int> q; void bfs(int x,int s,int w){ q.push(x); while(!q.empty()){ int x=q.front();q.pop();f[s][x]=w; for(int i=0;i<G[x].size();i++) if(!f[s][G[x][i]])q.push(G[x][i]); G[x].clear(); } return; } void bgs(int x,int s,int w){ q.push(x); while(!q.empty()){ int x=q.front();q.pop();g[s][x]=w; for(int i=0;i<D[x].size();i++) if(!g[s][D[x][i]])q.push(D[x][i]); D[x].clear(); } return; } int main() { scanf("%d%d",&n,&m);m++; for(int i=2;i<=m;i++) scanf("%d%d",&ex[i],&ey[i]); reverse(ex+2,ex+1+m); reverse(ey+2,ey+1+m); for(int x=1;x<=n;x++){ for(int i=1;i<=n;i++)G[i].clear(); for(int i=1;i<=n;i++)D[i].clear(); f[x][x]=g[x][x]=1; for(int i=2;i<=m;i++){ if(ex[i]<x||ey[i]<x)continue; if(f[x][ex[i]]&&!f[x][ey[i]])bfs(ey[i],x,i); else if(!f[x][ex[i]]&&!f[x][ey[i]])G[ex[i]].push_back(ey[i]); if(g[x][ey[i]]&&!g[x][ex[i]])bgs(ex[i],x,i); else if(!g[x][ey[i]]&&!g[x][ex[i]])D[ey[i]].push_back(ex[i]); } } for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) if(f[i][j]&&g[i][j]) ans[max(f[i][j],g[i][j])]++; for(int i=1;i<=m;i++)ans[i]+=ans[i-1]; reverse(ans+1,ans+1+m); for(int i=1;i<=m;i++) printf("%d ",ans[i]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15600506.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(41)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示