题目:
给定一个
n
n
n个结点,
m
m
m条边的有向图,现在从结点1出发,最后回到结点1,每个结点有权值1,访问结点时可以获得该权值,可以经过一个结点多次,但只能获得一次权值,在这个过程中有最多1次机会可以逆向走边,问最后可以得到的最大权值和是多少。
(
1
≤
n
,
m
≤
100000
)
(1 \le n,m \le 100000)
(1≤n,m≤100000)
题解:
看到这种可以经过一个结点多次,但一个结点只算一次贡献,就可以想到需要
t
a
r
j
a
n
tarjan
tarjan求强连通分量,然后缩点。缩完点以后图变成了一个
D
A
G
DAG
DAG(下面所说的原图和结点均指缩点后的图),这种有特殊条件的最长路需要用分层图的思想。将状态(结点编号
n
o
d
e
node
node,访问至该结点有无使用过逆向机会
f
l
a
g
flag
flag)看成新图的结点。然后建图:假设原图
u
→
v
u \rightarrow v
u→v,则
(
u
,
0
)
→
(
v
,
0
)
,
(
u
,
1
)
−
>
(
v
,
1
)
,
(
v
,
0
)
→
(
u
,
1
)
(u,0) \rightarrow (v,0),(u,1)->(v,1),(v,0) \rightarrow (u,1)
(u,0)→(v,0),(u,1)−>(v,1),(v,0)→(u,1),跑
s
p
f
a
spfa
spfa求最长路即可(变负号跑
d
i
j
k
s
t
r
a
dijkstra
dijkstra也行,因为这样建图是没有环的),最后的答案就是
d
i
s
s
c
c
1
,
1
dis_{scc_1,1}
disscc1,1,
s
c
c
1
scc_1
scc1为结点1所在的强联通分量编号。
那么这种做法是怎么保证同一个点只算一次贡献的呢?由于缩点完的图是一个
D
A
G
DAG
DAG,所以在同一层相同的点不会在一条路径中出现多次,不然就有环了,那么从0层跳到1层以后怎么保证0层已经在路径中的点不会在1层中再经过一次呢?0层中已经在路径中的点是不可能到达结点1的,因为在0层是从结点1出发的,如果又可以回到结点1,就和
D
A
G
DAG
DAG矛盾了;而在1层中要到达结点1,所以如果在到达结点1的路径中又经过了0层中的点,按照之前的结论,就不可能到达结点1了。所以可以保证一个结点在从结点1到结点1的过程中最多只会经过1次。
基于以上的分析,这种做法对于从结点1到其他点的答案是不对的。
复杂度: O ( 能 过 ) O(能过) O(能过)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,num,tp,sccnum;
vector<int>g[maxn],g2[maxn],r[maxn];
int dfn[maxn],low[maxn],stk[maxn],dis[maxn][2],inq[maxn][2],we[maxn],scc[maxn];
struct node{
int no,f,d;
};
queue<node>q;
void tarjan(int u){
dfn[u]=low[u]=++num;
stk[++tp]=u;
for(auto v:g[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!scc[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
++sccnum;
while(stk[tp]!=u){
scc[stk[tp]]=sccnum;
we[sccnum]++;
--tp;
}
scc[stk[tp]]=sccnum;
we[sccnum]++;
--tp;
}
}
void rebuild(){
for(int u=1;u<=n;u++){
for(auto v:g[u]){
int nu=scc[u],nv=scc[v];
if(nu!=nv){
g2[nu].pb(nv);
r[nv].pb(nu);
}
}
}
}
void spfa(){
for(int i=1;i<=n;i++){
for(int j=0;j<2;j++){
dis[i][j]=-INF;
}
}
dis[scc[1]][0]=0;
q.push(node{scc[1],0,dis[scc[1]][0]});
inq[scc[1]][0]=1;
while(!q.empty()){
node u=q.front();
q.pop();
inq[u.no][u.f]=0;
for(auto v:g2[u.no]){
if(dis[u.no][u.f]+we[v]>dis[v][u.f]){
dis[v][u.f]=dis[u.no][u.f]+we[v];
if(!inq[v][u.f]){
q.push(node{v,u.f,dis[v][u.f]});
inq[v][u.f]=1;
}
}
}
if(u.f==0){
for(auto v:r[u.no]){
if(dis[u.no][u.f]+we[v]>dis[v][1]){
dis[v][1]=dis[u.no][u.f]+we[v];
if(!inq[v][1]){
q.push(node{v,1,dis[v][1]});
inq[v][1]=1;
}
}
}
}
}
}
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
int u,v;
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
g[u].pb(v);
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
tarjan(i);
}
}
rebuild();
spfa();
printf("%d\n",dis[scc[1]][1]);
return 0;
}