zoukankan      html  css  js  c++  java
  • 【洛谷3119】[USACO15JAN] Grass Cownoisseur G(Tarjan+SPFA)

    点此看题面

    • 给定一张(n)个点(m)条边的有向图。
    • (1)号点出发走回(1)号点,可以走至多一次反向边,问最多能经过多少节点。
    • (n,mle10^5)

    (Tarjan)

    不考虑可以走反向边这一条件,那么答案就是(1)号点所在强连通分量的点数。

    众所周知强连通分量可以用(Tarjan)

    其实也正是为了复习(Tarjan)我才会来打这道题的。。。

    总而言之,我们先对整张图跑遍(Tarjan)缩一次点。

    走反向边

    考虑走反向边,必然是从(1)号点所在强连通分量向下走到某一个点,然后走反向边回到(1)号点之前的一个点,再走回到(1)号点所在的强连通分量。

    我们枚举反向边((x,y)),令(u_i)(1)号点到其后面每个点的最长路,(v_i)(1)号点到其前面每个点的最长路,那么答案就是最大的(Sz_{c_1}+u_x+v_y)

    想了半天不太清楚怎么从(DAG)中间开始拓扑。。。

    于是只好暴力地写了个(SPFA)求最长路了。

    代码:(O(SPFA))

    #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 100000
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    using namespace std;
    int n,m;
    struct Graph
    {
    	int ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
    	I void add(CI x,CI y) {e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y;}
    	I int operator [] (CI x) Con {return lnk[x];}
    	I edge operator () (CI x) Con {return e[x];}
    }G,nG,iG;
    namespace Tarjan//Tarjan缩点
    {
    	int d,dfn[N+5],low[N+5],T,S[N+5],IS[N+5],cnt,c[N+5],Sz[N+5];
    	I void dfs(CI x)
    	{
    		dfn[x]=low[x]=++d,IS[S[++T]=x]=1;for(RI i=G[x];i;i=G(i).nxt) dfn[G(i).to]?
    			IS[G(i).to]&&Gmin(low[x],dfn[G(i).to]):(dfs(G(i).to),Gmin(low[x],low[G(i).to]));
    		if(dfn[x]==low[x]) {++cnt;W(++Sz[c[S[T]]=cnt],IS[S[T]]=0,S[T--]^x);}
    	}
    }
    namespace SPFA//SPFA求最长路
    {
    	int IQ[N+5];queue<int> q;
    	int u[N+5],v[N+5];I void Work(Con Graph& G,int* dis,CI x)
    	{
    		RI i,k,y,t;dis[x]=0,q.push(x);W(!q.empty())
    			for(i=G[k=q.front()],q.pop(),IQ[k]=0;i;i=G(i).nxt) y=G(i).to,
    				dis[y]<(t=dis[k]+Tarjan::Sz[y])&&(dis[y]=t,!IQ[y]&&(q.push(y),IQ[y]=1));
    	}
    }
    int main()
    {
    	using namespace Tarjan;using namespace SPFA;
    	RI i,j,x,y;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),G.add(x,y);
    	for(i=1;i<=n;++i) !dfn[i]&&(dfs(i),0);for(i=1;i<=n;++i) for(j=G[i];//建新图
    		j;j=G(j).nxt) c[i]^c[G(j).to]&&(nG.add(c[i],c[G(j).to]),iG.add(c[G(j).to],c[i]),0);
    	memset(u,-1,sizeof(u)),memset(v,-1,sizeof(v)),Work(nG,u,c[1]),Work(iG,v,c[1]);//跑两侧最长路
    	RI t=Sz[c[1]];for(i=1;i<=cnt;++i) if(~u[i])
    		for(j=iG[i];j;j=iG(j).nxt) ~v[iG(j).to]&&Gmax(t,Sz[c[1]]+u[i]+v[iG(j).to]);//枚举反向边统计答案
    	return printf("%d
    ",t),0;
    }
    
  • 相关阅读:
    JavaScript 中的面向对象编程
    LINUX --- echo修改GPIO状态
    Ubuntu安装海思SDK(转)
    一个C++版本的Sqlite3封装--SmartDb
    分享一个内网穿透工具frp
    opencv图片缩放与镜像
    和菜鸟一起学linux之DBUS基础学习记录(转)
    谈谈嵌入式系统中多进程设计与进程通信
    高效c/c++日志工具zlog使用介绍
    udevd启动失败问题
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3119.html
Copyright © 2011-2022 走看看