洛谷 P3119 题解
题目传送门
题意简述:
- \(n\)个点,\(m\)条有向边,求从点\(1\)出发最后回到点\(1\)最多能经过的点数
思路:
-
首先想到\(tarjan\)缩点,因为可以想到只要走到一个连通块里面,则一定可以走完这个连通块
-
缩点之后,若是不用走反边,答案即为点\(1\)所在强连通分量点数
-
若是要走反边:
-
先从点\(1\)所在连通块出发,求出能到达每个连通块的最长路;再跑一遍反图,求出到达每个连通块的最长路(每条边指向翻转不会影响强连通分量)。
-
(这里考试没细想,直接想用DFS,并且还没更新最长路……寄的很惨……可惜了)
-
(事实上这个最长路拓扑,SPFA等都是可以的)
-
最后枚举连通块之间的每条边,若是这条边的起点能到达点\(1\)(即刚才跑反图能从点\(1\)到达这里),终点能够从点\(1\)来到,那么把这两个的最长路径之和减去点\(1\)所在连通块点数即可
- (即\(dis2[now]+dis1[to]-num[belong[1]]\))
-
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<vector>
#include<bitset>
#include<queue>
#define int long long
#define maxn 100005
using namespace std;
void read(int &n){
n=0;int f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
n=(n<<3)+(n<<1)+ch-'0';
ch=getchar();
}
n*=f;
}
struct EDGE{
int to,next;
bool opt;
}edge[maxn<<2];
int head[maxn<<2];
int cnt_edge=0;
void init(int n){
for(int i=1;i<=n;i++) head[i]=-1;
}
void add(int u,int v,bool pd){
if(pd==true) edge[cnt_edge].opt=true;
else edge[cnt_edge].opt=false;
edge[cnt_edge].to=v;
edge[cnt_edge].next=head[u];
head[u]=cnt_edge++;
}
int n,m;
int dfn[maxn],low[maxn],cnt_tar=0;
stack <int> st;bool inz[maxn];
void init2(){for(int i=0;i<maxn;i++) inz[i]=false;}
int belong[maxn],num[maxn],cnt_be=0;
void tarjan(int rt){
dfn[rt]=++cnt_tar;low[rt]=dfn[rt];
st.push(rt);inz[rt]=true;
int to;
for(int i=head[rt];i>=0;i=edge[i].next){
if(edge[i].opt==false) continue;
to=edge[i].to;
if(!dfn[to]){
tarjan(to);
low[rt]=min(low[rt],low[to]);
}else if(inz[to]==true){
low[rt]=min(low[rt],dfn[to]);
}
}
int now=-1;
if(dfn[rt]==low[rt]){
cnt_be++;
while(now!=rt){
now=st.top(),st.pop();
inz[now]=false;
belong[now]=cnt_be;
num[cnt_be]++;
}
}
return ;
}
struct EDGE_VE{
int to;
bool opt;
};
vector <EDGE_VE> g[maxn];
int fro[maxn],too[maxn];
int in[maxn],ou[maxn];
bool vis[maxn];
void dfs1(int rt,bool pd){
vis[rt]=true;
int to;
for(int j=0;j<g[rt].size();j++){
if(g[rt][j].opt!=pd) continue;
to=g[rt][j].to;
if(!vis[to]){
dfs1(to,pd);
}
}
}
void topo1(int rt,int pd){
memset(in,0,sizeof(in));
memset(ou,0,sizeof(ou));
int to;
for(int i=1;i<=cnt_be;i++){
if(!vis[i]) continue;
for(int j=0;j<g[i].size();j++){
to=g[i][j].to;
if(g[i][j].opt==pd&&vis[to]==true){
ou[i]++,in[to]++;
}
}
}
queue <int> q;
q.push(belong[1]);
fro[belong[1]]=num[belong[1]];
int now;
while(!q.empty()){
now=q.front(),q.pop();
for(int j=0;j<g[now].size();j++){
if(g[now][j].opt==pd){
to=g[now][j].to;
fro[to]=max(fro[to],fro[now]+num[to]);
in[to]--;
if(!in[to]){
q.push(to);
}
}
}
}
}
void dfs2(int rt,bool pd){
vis[rt]=true;
int to;
for(int j=0;j<g[rt].size();j++){
if(g[rt][j].opt!=pd) continue;
to=g[rt][j].to;
if(!vis[to]){
dfs2(to,pd);
}
}
}
void topo2(int rt,int pd){
memset(in,0,sizeof(in));
memset(ou,0,sizeof(ou));
int to;
for(int i=1;i<=cnt_be;i++){
if(!vis[i]) continue;
for(int j=0;j<g[i].size();j++){
to=g[i][j].to;
if(g[i][j].opt==pd&&vis[to]){
ou[i]++,in[to]++;
}
}
}
queue <int> q;
q.push(belong[1]);
too[belong[1]]=num[belong[1]];
int now;
while(!q.empty()){
now=q.front(),q.pop();
for(int j=0;j<g[now].size();j++){
if(g[now][j].opt==pd){
to=g[now][j].to;
too[to]=max(too[to],too[now]+num[to]);
in[to]--;
if(!in[to]){
q.push(to);
}
}
}
}
}
signed main()
{
// freopen("wander.in","r",stdin);
// freopen("wander.out","w",stdout);
read(n),read(m);
init(n);
init2();
int a,b;
for(int i=1;i<=m;i++){
read(a),read(b);
add(a,b,true);
add(b,a,false);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i);
}
int to;
for(int i=1;i<=n;i++){
for(int j=head[i];j>=0;j=edge[j].next){
if(belong[i]==belong[edge[j].to]) continue;
g[belong[i]].push_back((EDGE_VE){belong[edge[j].to],edge[j].opt});
}
}
dfs1(belong[1],true);
topo1(belong[1],true);
memset(vis,0,sizeof(vis));
dfs2(belong[1],false);
topo2(belong[1],false);
int ans=num[belong[1]];
for(int i=1;i<=cnt_be;i++){
for(int j=0;j<g[i].size();j++){
if(g[i][j].opt==true) continue;
to=g[i][j].to;
if(!too[to]||!fro[i]) continue;
ans=max(ans,too[to]+fro[i]-num[belong[1]]);
}
}
printf("%lld",ans);
return 0;
}
原发布于 \(2023.2.5\)