zoukankan      html  css  js  c++  java
  • luogu P1726 上白泽慧音

    上白泽慧音

    2017-09-19


    题目描述

    在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。


    输入输出格式

    输入格式:

    第1行:两个正整数N,M

    第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。

    输出格式:

    第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。

    第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。


    输入输出样例

    输入样例#1:
    5 5
    1 2 1
    1 3 2
    2 4 2
    5 1 2
    3 5 1
    
    输出样例#1:
    3
    1 3 5
    

    说明

    对于60%的数据:N <= 200且M <= 10,000

    对于100%的数据:N <= 5,000且M <= 50,000


    强连通分量裸题

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<stack>
    using namespace std;
    const int maxn=5000+999;
    int read(){
        int an=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while('0'<=ch&&ch<='9'){an=an*10+ch-'0';ch=getchar();}
        return an*f;
    }
    vector<int>b[maxn];
    bool in[maxn];
    int id[maxn],cnt,ans_T,P[maxn],low[maxn];
    int ans[maxn],n,m;
    stack<int>q;
    void add(int x,int y){
        b[x].push_back(y);
    }
    void tarjan(int x){
        cnt++;
        low[x]=id[x]=cnt;
        q.push(x);in[x]=1;
        for(int i=0;i<b[x].size();i++){
            int v=b[x][i];
            if(!id[v]){
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else if(in[v]){
                low[x]=min(low[x],id[v]);
            }
        }
        if(id[x]==low[x]){
            int cnt1=0;
            while(q.top()!=x){
                cnt1++;
                in[q.top()]=0;
                ans[cnt1]=q.top();
                q.pop();
            }
            cnt1++;
            ans[cnt1]=q.top();
            in[q.top()]=0;
            q.pop();
            if(cnt1>ans_T){
                ans_T=cnt1;
                for(int i=1;i<=cnt1;i++)
                P[i]=ans[i];
                sort(P+1,P+1+cnt1);
            }
        }
    }
    int main(){
        n=read();m=read();
        for(int i=1;i<=m;i++){
            int x,y,z;
            x=read();y=read();z=read();
            add(x,y);
            if(z==2)add(y,x);
        }
        for(int i=1;i<=n;i++)if(!id[i])tarjan(i);
        cout<<ans_T<<endl;
        for(int i=1;i<=ans_T;i++)cout<<P[i]<<" ";
    }
    老师

    by:s_a_b_e_r


    Tarjan裸题+1

    先安静的跑一遍Tarjan板子,对每一个搜到的SCC进行判断,如果大于当前答案那么更新答案

    byvoid ←dalao的Tarjan讲解

     

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    #include<vector>
    using namespace std;
    const int N=5009,M=500009;
    int n,m,cnt,p[N],ans;
    int ind,dfn[N],low[N];
    bool in[N];
    struct edge{int to,nex;}e[M<<1];
    stack<int>s;
    vector<int>an,scc;
    void add(int u,int v){
        e[++cnt]=(edge){v,p[u]};
        p[u]=cnt;
    }
    void tarjan(int u)
    {
        dfn[u]=low[u]=++ind;
        in[u]=1;s.push(u);
        for(int i=p[u];i;i=e[i].nex)
        {
            int v=e[i].to;
            if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
            else if(in[v])low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u])
        {
            int v=0;
            scc.clear();
            while(v!=u)
            {
                v=s.top();s.pop();
                in[v]=0;
                scc.push_back(v);
            }
            if(scc.size()>an.size())
            {
                ans=scc.size();
                sort(scc.begin(),scc.end());
                an.clear();
                for(int i=0;i<scc.size();++i)
                an.push_back(scc[i]);
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i)
        {
            int a,b,t;
            scanf("%d%d%d",&a,&b,&t);
            if(t==1)add(a,b);
            else{add(a,b);add(b,a);}
        }
        for(int i=1;i<=n;++i)
        if(!dfn[i])tarjan(i);
        cout<<ans<<endl;
        for(int i=0;i<ans;++i)cout<<an[i]<<" ";
        return 0;
    }
    teacher☆
    1.算法简介
    
    官方(?)定义——“Tarjan 算法:一种由Robert Tarjan提出的求解有向图强连通分量的算法。”
    ……什么叫“强连通分量”?
    这玩意网上真是怎么说的都有啊,查到了好多东西,稍微整合一下……
    在有向图G中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点“强连通”。
    如果有向图G的每两个顶点都强连通,那么称G是一个“强连通图”。
    如果这个有向图G中的一个子图满足每两个点强连通,那么我们把这个子图称为“强连通子图”。(单独一个点也是)
    G中的一个极大强连通子图(使最大的环最大),称为一个“强连通分量”。
    (可以理解为环,孤立的点是自环)
    
    //后期:↑上面这段我自己都不知道我自己在说啥……什么时候语文好了改下
    
    tarjan里还有一些其他需要用到的定义:
    dfn[i]:在dfs中该节点被搜索的次序(又名时间戳)
    low[i]表示i所能直接或间接达到的、dfn值最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)
    当dfn[i]==low[i]时,以i为根的搜索子树上所有节点是一个强连通分量。
    
    
    2.算法流程
    
    其实说白了还是dfs。
    除此之外要用到栈……指的不是递归中的系统栈,是自己加的栈。
    
    首先定义一些dfs树上的边的概念……
    树枝边:指dfs过程中的边
            ——就是构成dfs树的边。
    横叉边:连接的两个点没有父子关系的边
            ——遇到横叉边怎么办?
            ——采取无视大法。(当然这是后话
    后向边:由子节点指向父节点的边
            ——只有这种边能产生环(即产生强连通子图)。
    
    从起始点开始dfs。
    每dfs到一个点u,标记dfn,low赋初值=dfn,把u入栈。
    然后,开始枚举从u出发的每条边,检查这条边指向的点v。
    如果v没有被访问过的话,说明这条边是树枝边,dfs这个点v(递归),递归结束回溯时更新low[u] = min(low[u], low[v])
    如果v还在栈中的话,说明这条边是后向边,更新low[u] = min(low[u], dfn[v])
    除此之外的情况,即v已经进过栈并且出了栈的情况,说明这条边是横叉边,既不能向下dfs,也不会产生环,于是采取无视大法OvO
    以上处理完后,如果u的dfn值依然等于low值的话,即u点所能指向的最小的点是它本身
    只有两种情况能做到这一点
    其一,这是孤立的一个点;
    其二,形成了一个指回u点的环,所以u及u的dfs子树是一个强连通分量。
    这时候就可以把栈中u及u的子树项(如果有的话)全部弹出了。
    一开始推荐的那个网页里有详细的算法流程演示,超清楚qwq。
    很久以前的小w写过的Tarjan笔记

    by:wypx


  • 相关阅读:
    敏捷开发
    Response.Write(js脚本)后 Response.redirect(...),为什么js脚本不执行,怎么解决!
    kaixin.com一波三折看SNS
    JavaScript数组的自定义 sort方法
    简单明了的SQL join解释
    [官方资料] 介绍 JSON
    SQL UNION 和 UNION ALL 操作符
    JavaScript frames 对象
    理解hasOwnProperty
    asp.net网站安全常见问题与防范
  • 原文地址:https://www.cnblogs.com/ck666/p/7565189.html
Copyright © 2011-2022 走看看