洛谷3119 草鑒定(tarjan)
阿新 • • 發佈:2018-12-22
假設 但是 mem main edge 存在 -- 逆向 line
題目大意
約翰有\(n\)塊草場,編號\(1\)到\(n\),這些草場由若幹條單行道相連。奶牛貝西是美味牧草的鑒賞家,她想到達盡可能多的草場去品嘗牧草。
貝西總是從\(1\)號草場出發,最後回到\(1\)號草場。她想經過盡可能多的草場,貝西在通一個草場只吃一次草,所以一個草場可以經過多次。因為草場是單行道連接,這給貝西的品鑒工作帶來了很大的不便,貝西想偷偷逆向行走一次,但最多只能有一次逆行。問,貝西最多能吃到多少個草場的牧草。
\(n,m\le 10^5\)
QwQ一開始看這個題 沒有思路呀
首先一定是\(tarjan\)消環,對吧
我們可以考慮,如果只能反向走一條邊,那我們可以枚舉這個邊呀,然後算一算\(ans\)
那麽對於一條邊\(u->v\),如果我們選擇反向走,我們能獲得的收益是\(val[v]+valn[u]-sval[1]\) 其中\(val[x]\)表示從1到x的最大收益,\(valn[x]\)表示\(x\)到1的最大收益(這個可以通過建反圖來算)
之所以減去\(sval[1]\),因為1這個聯通快的貢獻會算兩邊,按照題意,應該只算一遍。
為什麽這樣是對,為什麽可以保證沒有別的點的貢獻被算兩遍。
我們可以這麽考慮,假設存在一個聯通快他的貢獻被計算了兩次,那麽他一定能到1,也能從1到,那麽就說明存在環,但是因為我們在一開始\(tarjan\)縮點過,所以不會存在這麽一個點,所以這樣計算貢獻是沒有錯的
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();} while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();} return x*f; } const int maxn = 1e5+1e2; const int maxm = 1e6+1e2; int point[maxn],nxt[maxm],to[maxm],sval[maxn]; int s[maxn],top; int bel[maxn],roo[maxn]; int tot; int cnt; int n,m; int x[maxm],y[maxm]; int low[maxn],dfn[maxn]; int vis[maxn],scc; void addedge(int x,int y) { nxt[++cnt]=point[x]; to[cnt]=y; point[x]=cnt; } void tarjan(int x) { dfn[x]=low[x]=++tot; s[++top]=x; vis[x]=1; for (int i=point[x];i;i=nxt[i]) { int p = to [i]; if (!dfn[p]) { tarjan(p); low[x]=min(low[x],low[p]); } else if(vis[p]) low[x]=min(low[x],dfn[p]); } if (low[x]==dfn[x]) { scc++; while (s[top+1]!=x) { //++scc; bel[s[top]]=scc; roo[s[top]]=x; sval[scc]++; vis[s[top]]=0; top--; } } } int num[maxm]; int dis[maxn],disn[maxn]; queue<int> q; void spfa(int s) { memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); vis[s]=1; dis[s]=sval[bel[s]]; q.push(s); while (!q.empty()){ int x = q.front(); q.pop(); vis[x]=0; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (dis[p]<dis[x]+sval[bel[p]]) { dis[p]=dis[x]+sval[bel[p]]; if (!vis[p]) { vis[p]=1; q.push(p); } } } } } void spfa1(int s) { memset(disn,0,sizeof(disn)); memset(vis,0,sizeof(vis)); vis[s]=1; disn[s]=sval[bel[s]]; q.push(s); while (!q.empty()){ int x = q.front(); q.pop(); vis[x]=0; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (disn[p]<disn[x]+sval[bel[p]]) { disn[p]=disn[x]+sval[bel[p]]; if (!vis[p]) { vis[p]=1; q.push(p); } } } } } int main() { n=read(),m=read(); for (int i=1;i<=m;i++) { x[i]=read(),y[i]=read(); addedge(x[i],y[i]); } for (int i=1;i<=n;i++) { if (!dfn[i]) tarjan(i); } //for (int i=1;i<=n;i++) cout<<sval[i]<<endl; memset(point,0,sizeof(point)); cnt=0; for (int i=1;i<=m;i++) { if (bel[x[i]]!=bel[y[i]]) { addedge(roo[x[i]],roo[y[i]]); num[i]=1; } } spfa(roo[1]); memset(point,0,sizeof(point)); cnt=0; for (int i=1;i<=m;i++) { if (num[i]) addedge(roo[y[i]],roo[x[i]]); } spfa1(roo[1]); int ans=0; //for (int i=1;i<=n;i++) cout<<dis[i]<<" "<<disn[i]<<endl; for (int i=1;i<=m;i++) { if (!num[i]) continue; if (dis[roo[y[i]]] && disn[roo[x[i]]]) ans=max(ans,dis[roo[y[i]]]+disn[roo[x[i]]]-sval[bel[roo[1]]]); } cout<<ans; return 0; }
洛谷3119 草鑒定(tarjan)