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


  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/ck666/p/7565189.html
Copyright © 2011-2022 走看看