题目大意有n个草场,有向图连接,从第一个草场出发,最终回到第一个草场。每经过一个草场就可以吃一次草,问假如可以偷偷逆向走一条道路,最多可以吃到多少次草
刚看到这道题,第一想法就是Tarjan缩点,求强连通分量。但是题目有个额外要求,可以有一次的机会逆向走一条道路。刚开始有点不会处理这个额外的要求。有模糊的想法但不对,看到别人的题解后感觉可以行的通。先进行Tarjan缩点,构造一个DAG图,然后分类,枚举。做出分层图。(头一回见分层图这个东东)
做法我就直接粘出来了。考虑一张图,将这个图复制一份,点的编号从1~N到(N+1)~(N+N)。然后在两层图中连边。对于原图上的每一条边,从原图的指向点到新图的起始点连一条边,(我的理解是即若原图u -> v有一条边,那么分层图中原图【下面的图】到新图【上面的图】存在v -> u'要连一条边)边权与原边相同,代表逆向走一条边。逆向走了一条边,就不能再逆向走了,所以从上面的一层(新图)无法回到下面的一层。最后跑一遍SPFA,节点1所在的强连通分量编号,到节点1所在的强连通分量编号+N上的最长路,就是最后的答案。
大佬还提供了一份扩展用这样的思想,还可以解决一道这样的问题:给定一个有向图G,有m张优惠券,可以把一条边的边权改成一个固定值k,求节点s到节点t之间最短路的长度和方案数。方法是建立一个m+1层的多层图,层与层之间的边的权值都为k,然后跑最短路。由于用了优惠券不一定能达到优化的目的,所以答案为min(t,t+n,t+n+n,...)。方案数也可如此做。
在此感谢那位博主。https://www.luogu.org/blog/hsfzLZH1/solution-p3119
/*
*looooop
* Do not go gentle into that good night
* -Dylan Thomas
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits>
using namespace std;
#define lson 2*i
#define rson 2*i+1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
#define MAX 200010
vector<int>G[MAX*2];
vector<int>re_G[MAX*2];
vector<int>DAG[MAX*2];
queue<int>q;
int dis[MAX];
int vis[MAX*2];
int dfn[MAX];
int low[MAX];
int color[MAX];
int belong[MAX];
int sum[MAX];
int n,m;
int tot;
int colorNum;
stack<int>St;
void init(){
MEM(vis,0);
MEM(dfn,0);
MEM(low,0);
MEM(dis,0);
MEM(sum,0);
MEM(belong,0);
}
void Tarjan(int st){
dfn[st] = low[st] = ++tot;
vis[st] = 1;
St.push(st);
for(int i = 0; i < G[st].size(); i++){
int v = G[st][i];
if(!dfn[v]){
Tarjan(v);
low[st] = min(low[st],low[v]);
}
else if(vis[v]){
low[st] = min(low[st],dfn[v]);
}
}
if(dfn[st] == low[st]){
//int colorNum = 0;
colorNum++;
while(1){
int now = St.top();
sum[colorNum]++;
belong[now] = colorNum;
vis[now] = 0;
St.pop();
if(st == now) break;
}
}
}
int main(int argc,char *argv[]){
scanf("%d%d",&n,&m);
for(int i = 1; i <= m ;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
//G[v].push_back(u);
//re_G[v].push_back(u);
}
init();
for(int i = 1; i <= n; i++){
if(!dfn[i]) Tarjan(i);
}
for(int i = 1; i <= colorNum; i++){
sum[colorNum+i] = sum[i];
}
for(int i = 1; i <= n; i++){
for(int j = 0; j < G[i].size(); j++){
int v = G[i][j];
if(belong[i] != belong[v]){
re_G[belong[i]].push_back(belong[v]);
re_G[belong[v]].push_back(belong[i]+colorNum);
re_G[belong[i]+colorNum].push_back(belong[v]+colorNum);
}
}
}
MEM(vis,0);
vis[belong[1]] = 1; q.push(belong[1]);
while(!q.empty()){
int x = q.front();
for(int i = 0; i < re_G[x].size(); i++){
int v = re_G[x][i];
if(dis[v] < dis[x]+sum[x]){
dis[v] = dis[x] + sum[x];
if(!vis[v]){
vis[v] = 1;
q.push(v);
}
}
}
q.pop();
vis[x] = 0;
}
printf("%d\n",dis[belong[1]+colorNum]);
return 0;
}