1. 程式人生 > >洛谷 P3119草鑑定

洛谷 P3119草鑑定

題目大意有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;
}