1. 程式人生 > >洛谷3119 草鑑定(tarjan)

洛谷3119 草鑑定(tarjan)

題目大意
約翰有\(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;
}