【一本通】强连通分量

学习Tarjan

 

受欢迎的牛

因为牛之间的关系具有传递性,所以找出有向图中的强连通子图,每个子图中的牛一定互相喜欢。

$Tarjan$缩点,只有出度为$0$的点(牛)才有可能受到其他所有牛的喜欢(图中已无环)。

如果出度为$0$的点不止$1$个,那么这几个子图中的牛都无法互相喜欢,输出$0$。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=5e4+5;
int n,m,cnt,top,fro[N],s[N];
int tot,color,dfs[N],low[N],col[N],size[N],in[N];
struct edge{int to,nxt;}e[M];
void add(int x,int y) {
    e[++cnt]={y,fro[x]}; fro[x]=cnt;
}

void Tarjan(int u) {
    dfs[u]=low[u]=++tot;
    s[++top]=u;
    for(int i=fro[u];i;i=e[i].nxt) {
        int v=e[i].to;
        if(!dfs[v]) {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!col[v])
         low[u]=min(low[u],dfs[v]);
    }
    if(low[u]==dfs[u]) {
        col[u]=++color;
        while(s[top]!=u) {
            col[s[top--]]=color;
            size[color]++;
        }
        top--,size[color]++;
    }
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1,a,b;i<=m;i++) 
     scanf("%d%d",&a,&b),add(b,a); //反向建边,统计出度变为统计入度 
    for(int i=1;i<=n;i++) 
     if(!dfs[i]) Tarjan(i);
     
    for(int i=1;i<=n;i++)
     for(int j=fro[i];j;j=e[j].nxt)
      if(col[i]!=col[e[j].to]) 
       in[col[e[j].to]]++;
    int ans=-1;
    for(int i=1;i<=color;i++)
     if(!in[i]) ans=~ans?0:size[i];
    printf("%d",ans);    
}

 

间谍网络

题解

#include<bits/stdc++.h>
using namespace std;
const int N=3e3+5,M=8e3+5;
int tot,top,color,s[N],dfn[N],low[N],col[N];
int n,p,r,cnt,ans,fro[N],money[N],mo[N],in[N];
struct edge{int to,nxt;}e[M];
void add(int x,int y) {
    e[++cnt]={y,fro[x]}; fro[x]=cnt;
}

void Tarjan(int u) {
    dfn[u]=low[u]=++tot;
    s[++top]=u;
    for(int i=fro[u];i;i=e[i].nxt) {
        int v=e[i].to;
        if(!dfn[v]) {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!col[v]) 
         low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]) {
        col[u]=++color;
        mo[color]=money[u];
        while(s[top]!=u) {
            col[s[top--]]=color;
            mo[color]=min(mo[color],money[s[top]]);
        }
        top--;
    }
}

int main() {
    scanf("%d%d",&n,&p);
    memset(money,0x7f,sizeof(money));
    for(int i=1,u,m;i<=p;i++) {
        scanf("%d%d",&u,&m);
        money[u]=m;
    }
    scanf("%d",&r);
    for(int i=1,u,v;i<=r;i++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++)
     if(!dfn[i]&&money[i]<=2e4) Tarjan(i);
    for(int i=1;i<=n;i++)
     if(!dfn[i]) return printf("NO\n%d",i),0;

    for(int i=1;i<=n;i++)
     for(int j=fro[i];j;j=e[j].nxt) 
      if(col[i]!=col[e[j].to]) in[col[e[j].to]]++;
  
    for(int i=1;i<=color;i++) 
     if(!in[i]) ans+=mo[i];
    printf("YES\n%d",ans);
}   

 

最大半联通子图

缩点后重新连边,此时答案为新图上最长链的个数。

拓扑排序求出以各个点为终点的最长链的长度和个数,统计答案即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,m,X,C,K,f[N],g[N],q[N],ru[N],vis[N];
int tot,top,color,s[N],dfn[N],low[N],co[N],size[N];
int cnt,fro[N],fro2[N];
struct edge{int to,nxt;}e[M],e2[M];
void add1(int x,int y) { e[++cnt]={y,fro[x]}; fro[x]=cnt; } void add(int x,int y) { e2[++cnt]={y,fro2[x]}; fro2[x]=cnt; ru[y]++; } void Tarjan(int u) { dfn[u]=low[u]=++tot; s[++top]=u; for(int i=fro[u];i;i=e[i].nxt) { int v=e[i].to; if(!dfn[v]) { Tarjan(v); low[u]=min(low[u],low[v]); } else if(!co[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { co[u]=++color; while(s[top]!=u) { co[s[top--]]=color; size[color]++; } top--,size[color]++; } } void rebuild() { cnt=0; for(int i=1;i<=n;i++) for(int j=fro[i];j;j=e[j].nxt) if(co[i]!=co[e[j].to]) add(co[i],co[e[j].to]); } void DP() { int h=0,t=0; for(int i=1;i<=color;i++) { if(!ru[i]) q[t++]=i; f[i]=size[i],g[i]=1; } while(h!=t) { int u=q[h++],v; for(int i=fro2[u];i;i=e2[i].nxt) { ru[v=e2[i].to]--; if(!ru[v]) q[t++]=v; if(vis[v]==u) continue; //处理重边 if(f[u]+size[v]>f[v]) { f[v]=f[u]+size[v]; g[v]=g[u]; } else if(f[u]+size[v]==f[v]) g[v]=(g[v]+g[u])%X; vis[v]=u; } } } int main() { n=read(),m=read(),X=read(); for(int i=1;i<=m;i++) { int a=read(),b=read(); add1(a,b); } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); rebuild(),DP(); for(int i=1;i<=color;i++) { if(f[i]>K) K=f[i],C=g[i]; else if(f[i]==K) C=(C+g[i])%X; } printf("%d\n%d",K,C); }

 

posted @ 2019-02-17 13:51  YeLingqi  阅读(141)  评论(0编辑  收藏  举报