zoukankan      html  css  js  c++  java
  • 洛谷3119 草鉴定(tarjan)

    题目大意
    约翰有(n)块草场,编号(1)(n),这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

    贝西总是从(1)号草场出发,最后回到(1)号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

    (n,mle 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;
    }
    
    
  • 相关阅读:
    CSS 两个行内块元素,宽度相加刚好等于父盒子容器的元素,但第二个元素掉在第二行解决办法
    js 自定义获得类class和获得id
    学习笔记:BSGS(拔山盖世?)算法
    解题报告: luogu P1040
    解题报告:SP18155 ABSP1
    解题报告:luogu P1099
    Thussat 游记
    解题报告:luogu P2220
    学习笔记:卢卡斯定理(并没有学会)
    从线性筛到欧拉函数,你十有八九能懂吧!
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10160940.html
Copyright © 2011-2022 走看看