洛谷 P3119草鑑定
阿新 • • 發佈:2018-12-08
題目大意有n個草場,有向圖連線,從第一個草場出發,最終回到第一個草場。每經過一個草場就可以吃一次草,問假如可以偷偷逆向走一條道路,最多可以吃到多少次草
剛看到這道題,第一想法就是Tarjan縮點,求強連通分量。但是題目有個額外要求,可以有一次的機會逆向走一條道路。剛開始有點不會處理這個額外的要求。有模糊的想法但不對,看到別人的題解後感覺可以行的通。先進行Tarjan縮點,構造一個DAG圖,然後分類,列舉。做出分層圖。(頭一回見分層圖這個東東)
做法我就直接粘出來了。考慮一張圖,將這個圖複製一份,點的編號從1~N到(N+1)~(N+N)。然後在兩層圖中連邊。對於原圖上的每一條邊,從原圖的指向點到新圖的起始點連一條邊,(我的理解是即若原圖u -> v有一條邊,那麼分層圖中原圖【下面的圖】到新圖【上面的圖】存在v -> u'要連一條邊)
大佬還提供了一份擴充套件用這樣的思想,還可以解決一道這樣的問題:給定一個有向圖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; }