连通性总结

1、问矩阵连乘多少次可以每个值都大于0

cf402EStrictly Positive Matrix【tarjan前向星模板、矩阵】

矩阵乘法中有这样一个重要的步骤:a^k中a[i][j]如果是+说明从i点有正好走k步就可以到达j点的路(那么由于子环的存在>k的步数的路也存在)即i、j连通我们依次建边所有点对(大于0的)剩下的跑一边tarjan 
强连通分量的个数=1,则输出"yes"反之“no"

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int M=4e6+10,N=4e6+10;
    int head[N],ed;
    struct node{
        int to,next;
    }edge[M];
    int sta[N],vis[N],low[N],dfn[N],top; //,out[N]
    void init(){
        ed=0;
        memset(head,-1,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(sta,0,sizeof(sta));
    }
    void addedge(int a,int b){
        edge[ed].to=b;
        edge[ed].next=head[a];
        head[a]=ed++;
    }
    void tarbfs(int k,int cnt,int &num){
        vis[k]=1;
        low[k]=cnt;
        dfn[k]=cnt;
        sta[top++]=k;
        for(int i=head[k];i>-1;i=edge[i].next){
            if(vis[edge[i].to]==0) tarbfs(edge[i].to,++cnt,num);
            if(vis[edge[i].to]==1) low[k]=min(low[k],low[edge[i].to]);
        }
        if(dfn[k]==low[k]){
            ++num;
            while(top>0&&sta[top]!=k){
                top--;
                low[sta[top]]=num;
                vis[sta[top]]=2;
            }//
        }
    }
    int tarjan(int n){
        int num=0,cnt=1;
        top=0;
        memset(vis,0,sizeof(vis));
        memset(low,0,sizeof(low));
        for(int i=1;i<=n;i++){
            if(vis[i]==0) tarbfs(i,cnt,num);
        }
        return num;
    }  

    int main()
    {
       // freopen("cin.txt","r",stdin);
        int n,m;
        while(cin>>n){
            int a,b;
            init();
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    scanf("%d",&a);
                    if(i==j) continue;
                    if(a>0) addedge(i,j);
                }
            }
            int num=tarjan(n);
            if(num==1) puts("YES");
            else puts("NO");
        }
        return 0;
    }  

2、判断是否存在三角恋

hdu4324 Triangle LOVE【tarjan强连通分量基础题】

给定一个有向图并规定:每两个点之间一定有边,同时A指向B则B定不能指向A,反之亦然。 询问是否存在仅有三个点构成的环。

首先判断有向图中是否存在环马上有tarjan能够很好的解决。并且如果存在大于三个点以上的环的话肯定存在仅有三个点构成的环。 因为每两个点之间都有边,并且只有一个给定的指向,画几个图便可以推导出这样的结论。

证明: 假设存在一个n元环,因为a->b有边,b->a必定没边,反之也成立 所以假设有环上三个相邻的点a-> b-> c,那么如果c->a间有边,就已经形成了一个三元环,如果c->a没边,那么a->c肯定有边,这样就形成了一个n-1元环。。。。 所以只需证明n为4时一定有三元环即可,显然成立。
以知道在tarjan后或中判断是否存在大于等于三个点的强连通图————转自acdreamer博客讲解

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int M=4e6+10,N=2e3+10;
    int head[N],ed;
    char str[2006][2006];
    struct node{
        int to,next;
    }edge[M];
    int sta[N],vis[N],low[N],dfn[N],top; //,out[N]
    void init(){
        ed=0;
        memset(head,-1,sizeof(head));
        memset(edge,0,sizeof(edge));
        memset(sta,0,sizeof(sta));
    }
    void addedge(int a,int b){
        edge[ed].to=b;
        edge[ed].next=head[a];
        head[a]=ed++;
    }
    void tarbfs(int k,int cnt,int &num){
        vis[k]=1;
        low[k]=cnt;
        dfn[k]=cnt;
        sta[top++]=k;
        for(int i=head[k];i>-1;i=edge[i].next){
            if(vis[edge[i].to]==0) tarbfs(edge[i].to,++cnt,num);
            if(vis[edge[i].to]==1) low[k]=min(low[k],low[edge[i].to]);
        }
        if(dfn[k]==low[k]){
            ++num;
            while(top>0&&sta[top]!=k){
                //printf("%d  ",sta[top]);
                top--;
                low[sta[top]]=num;
                vis[sta[top]]=2;
            }//
        }
    }
    int tarjan(int n){
        int num=0,cnt=1;
        top=0;
        memset(vis,0,sizeof(vis));
        memset(low,0,sizeof(low));
        for(int i=1;i<=n;i++){
            if(vis[i]==0) tarbfs(i,cnt,num);
        }
        return num;
    }  

    int main()
    {
      //  freopen("cin.txt","r",stdin);
        int n,m,t,b;  

        scanf("%d",&t);
        b=1;
        while(t--){
            int a;
            init();
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
                scanf("%s",str[i]+1);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(str[i][j]=='1')
                        addedge(i,j);
            /*for(int i=1;i<=ed;i++)
            {
                for(int k=head[i];k!=-1;k=edge[k].next)
                    printf("u=%d v=%d    ",i,edge[k].to);
                printf("\n");
            }*/
            int num=tarjan(n);
            //printf("%d   ",num);  

            printf("Case #%d: ",b++);
            if(num>=1&&num!=n) puts("Yes");
            else puts("No");
        }
        return 0;
    }

3、等价性证明,白书例题。

hdu2767Proving Equivalences【STL版SCCTarjan+缩点】(有注释)

说白了就是问加多少边使得原图联通,跑强连通分量之后缩点,出度=0或者入度=0的最大值就是答案

    #include <iostream>
    #include<cstdio>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 20005
    vector<int>G[maxn];
    int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
    stack<int>S;
    int in0[maxn],out0[maxn];
    void dfs(int u)
    {
        pre[u]=lowlink[u]=++dfs_clock;
        S.push(u);<span style="font-family: Arial, Helvetica, sans-serif;">//每搜索到一个点,压入栈中</span>
        for(int i=0;i<G[u].size();i++)//遍历与p相连的点
        {
            int v=G[u][i];
            if(!pre[v])//不在栈中
            {
                dfs(v);
                lowlink[u]=min(lowlink[u],lowlink[v]);
            }
            else if(!sccno[v])//在栈中
                lowlink[u]=min(lowlink[u],pre[v]);
        }
        if(lowlink[u]==pre[u])//发现一个根
        {
            scc_cnt++;
            for(;;)
            {
                int x=S.top();S.pop();//词典以上的所有点全部出栈 构成一个强连通分量
                sccno[x]=scc_cnt;//scc_cnt是强连通分量的序号
                if(x==u) break;
            }
        }
    }
    void find_scc(int n)
    {
        dfs_clock=scc_cnt=0;
        memset(sccno,0,sizeof(sccno));
        memset(pre,0,sizeof(pre));
        for(int i=0;i<n;i++) if(!pre[i]) dfs(i);
    }
    int main()
    {
        int  T,n,m;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++) G[i].clear();
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);u--;v--;
                G[u].push_back(v);
            }
            find_scc(n);
            for(int i=1;i<=scc_cnt;i++) in0[i]=out0[i]=1;
            for(int u=0;u<n;u++)
            {
                for(int i=0;i<G[u].size();i++)
                {
                    int v=G[u][i];
                    if(sccno[u]!=sccno[v]) in0[sccno[v]]=out0[sccno[u]]=0;
                }
            }
            int a=0,b=0;
            for(int i=1;i<=scc_cnt;i++)
            {
                if(in0[i]) a++;
                if(out0[i]) b++;
            }
            int ans=max(a,b);
            if(scc_cnt==1) ans=0;
            printf("%d\n",ans);
        }
        return 0;
    }  

4、强连通分量入门题

poj2553The Bottom of a Graph【tarjan中SCC出度是1】

#include <iostream>
#include<cstdio>
#include<stack>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 5005
vector<int>G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int>S;
int in0[maxn],out0[maxn];
int num[5006];
void dfs(int u)
{
    pre[u]=lowlink[u]=++dfs_clock;
    S.push(u);
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!pre[v])
        {
            dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }
        else if(!sccno[v])
            lowlink[u]=min(lowlink[u],pre[v]);
    }
    if(lowlink[u]==pre[u])
    {
        scc_cnt++;
       // printf("序号是%d  ,里面有",scc_cnt);
        for(;;)
        {
            int x=S.top();S.pop();
            sccno[x]=scc_cnt;
            //printf("%d   ",x);
            if(x==u) break;
        }
    }
}
void find_scc(int n)
{
    dfs_clock=scc_cnt=0;
    memset(sccno,0,sizeof(sccno));
    memset(pre,0,sizeof(pre));
    for(int i=0;i<n;i++) if(!pre[i]) dfs(i);
}
int main()
{
   // freopen("cin.txt","r",stdin);
    int  n,m;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        scanf("%d",&m);
        for(int i=0;i<n;i++) G[i].clear();
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);u--;v--;
            G[u].push_back(v);
        }
        find_scc(n);
        for(int i=1;i<=scc_cnt;i++) in0[i]=out0[i]=1;
       // printf("scc_cnt=%d   ",scc_cnt);
        for(int u=0;u<n;u++)
        {
            for(int i=0;i<G[u].size();i++)
            {
                int v=G[u][i];
                if(sccno[u]!=sccno[v]) in0[sccno[v]]=out0[sccno[u]]=0;
            }
        }
        int a=0,cnt=0;
        for(int i=1;i<=scc_cnt;i++)
        {
            if(out0[i])
            {
               // printf("i=%d   ",i);
                for(int j=0;j<n;j++)
                {
                    if(sccno[j]==i)
                        num[cnt++]=j+1;
                }
            }
        }
        //printf("cnt=%d  ",cnt);
        if(cnt)
        {
            sort(num,num+cnt);
            printf("%d",num[0]);
            for(int i=1;i<cnt;i++) printf(" %d",num[i]);
        }
        printf("\n");
    }
    return 0;
}

5、强连通分量公主王子配对问题

poj1904King‘s Quest【SCC tarjan解决配对问题】

给出每个王子喜欢的公主,要求王子选择某个公主后,其他王子也都能选择喜欢的公主,并且给出了一组正解

为王子喜欢的公主建边,由于后来给出的正解一定是成立的,那么后来的条件用来反向建边,那么跑完tarjan,强连通分量内的彼此都是可行解(就是说,如果输出其中一对,其他的也可以满足条件)。如果满足”王子喜欢这个公主“的条件,那么就可以输出

#include <iostream>
#include<cstdio>
#include<stack>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 2005
vector<int>G[maxn<<1];
vector<int>ans;
int pre[maxn<<1],lowlink[maxn<<1],sccno[maxn<<1],dfs_clock,scc_cnt;
stack<int>S;
int ccount[maxn<<1];
bool exist[maxn<<1][maxn<<1];
void dfs(int u)
{
    pre[u]=lowlink[u]=++dfs_clock;
    S.push(u);
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!pre[v])
        {
            dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }
        else if(!sccno[v])
            lowlink[u]=min(lowlink[u],pre[v]);
    }
    if(lowlink[u]==pre[u])
    {
        scc_cnt++;
       // printf("序号是%d  ,里面有",scc_cnt);
        for(;;)
        {
            int x=S.top();S.pop();
            sccno[x]=scc_cnt;
           // ccount[scc_cnt]++;
           // printf("%d   ",x+1);
            if(x==u) break;
        }
    }
}
void find_scc(int n)
{
    dfs_clock=scc_cnt=0;
    memset(sccno,0,sizeof(sccno));
    memset(pre,0,sizeof(pre));
    for(int i=0;i<n;i++) if(!pre[i]) dfs(i);
}
int main()
{
 //   freopen("cin.txt","r",stdin);
    int n,k,a;
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++) G[i].clear();
       // memset(ccount,0,sizeof(ccount));
        memset(exist,false,sizeof(exist));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&a);
                a--;
                G[i].push_back(a+2000);
                exist[i][a+2000]=true;
            }
        }
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a);
            a--;
            G[a+2000].push_back(i);
            exist[a+2000][i]=true;
        }
        find_scc(n);
        /**********
        for(int i=0;i<n;i++)
        {
            printf("count=%d",ccount[sccno[i]]/2);
            for(int j=2000;j<2000+n;j++)
            {
                if(sccno[i]==sccno[j]) printf(" %d",j-2000+1);
            }
            printf("\n");
        }
        **********/
        for(int i=0;i<n;i++)
        {
            ans.clear();
            for(int j=2000;j<2000+n;j++)
            {
                if(exist[i][j]&&sccno[i]==sccno[j])
                    ans.push_back(j-2000);
            }
            printf("%d",ans.size());
            for(int j=0;j<ans.size();j++) printf(" %d",ans[j]+1);
            printf("\n");
        }
    }
    return 0;
}  

6、强联通分量水题

hdu1827Summer Holiday【tarjan强连通分量解决最小联系费用】

既然是强连通分量的题,很容易想到形成的东西是一坨一坨的,哈哈,然后如果某一坨入度为0,那么很不幸,这一坨只能直接被威士忌通知,至于具体通知这一坨中的哪一个,枚举一遍就知道了,最后把话费求和~

    #include <iostream>
    #include<cstdio>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 2020
    vector<int>G[maxn];
    int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
    stack<int>S;
    int in0[maxn],out0[maxn];
    int value[maxn];
    int cost;
    void dfs(int u)
    {
        pre[u]=lowlink[u]=++dfs_clock;
        S.push(u);
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(!pre[v])
            {
                dfs(v);
                lowlink[u]=min(lowlink[u],lowlink[v]);
            }
            else if(!sccno[v])
                lowlink[u]=min(lowlink[u],pre[v]);
        }
        if(lowlink[u]==pre[u])
        {
            scc_cnt++;
            for(;;)
            {
                int x=S.top();S.pop();
                sccno[x]=scc_cnt;
                if(x==u) break;
            }
        }
    }
    void find_scc(int n)
    {
        dfs_clock=scc_cnt=0;
        memset(sccno,0,sizeof(sccno));
        memset(pre,0,sizeof(pre));
        for(int i=0;i<n;i++) if(!pre[i]) dfs(i);
    }
    int main()
    {
        int  T,n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(int i=0;i<n;i++) G[i].clear();
            cost=0;
            for(int i=0;i<n;i++) scanf("%d",&value[i]);
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);u--;v--;
                G[u].push_back(v);
            }
            find_scc(n);
            for(int i=1;i<=scc_cnt;i++) in0[i]=out0[i]=1;
            for(int u=0;u<n;u++)
            {
                for(int i=0;i<G[u].size();i++)
                {
                    int v=G[u][i];
                    if(sccno[u]!=sccno[v]) in0[sccno[v]]=out0[sccno[u]]=0;
                }
            }
            int a=0,b=0;
            for(int i=1;i<=scc_cnt;i++)
            {
                if(in0[i])
                {
                    a++;
                    int maxvalue=0x3f3f3f3f;
                    for(int j=0;j<n;j++)
                    {
                        if(sccno[j]==i)
                        maxvalue=min(maxvalue,value[j]);
                    }
                    cost+=maxvalue;
                }
            }
            printf("%d %d\n",a,cost);
        }
        return 0;
    }  

7、双联通分量

hdu3394Railway【双连通分量+模板详细解释】

题意:给一个无向图。如果至少有两个环共用了一些边,那么这些边被认为是“冲突边”。如果一些边不在任何一个环中,这些边被认为是“多余边”。你要找出这个图中有多少“多余边”和“冲突边”然后输出条数。另外这图不一定是连通的

1。“多余边”不在任何一个环中,那么多余边一定是桥,所以统计这个无向图中有多少桥即可(这个好求)

2。对于每个联通分量中如果边数大于点数,那么所有的边都算冲突边(在原来模板上加入这样的话:将同一个双联通分量的所有边输入到同一个vector当中,这样方便求边数,也方便求点数)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <stack>  

    using namespace std;  

    const int N=10006;
    struct Edge
    {
        int st, en;
        Edge() {}
        Edge(int a, int b)
        {
            st=a, en=b;
        }
    };
    stack <Edge> palm;
    vector <int> arc[N];
    vector <Edge> block[N];
    int dfn[N], low[N];
    bool vs[N];
    int n, m, ind, T, sum1, sum2;  

    void tarjan(int u, int pre)
    {
        dfn[u]=low[u]=T++;
        int len=(int)arc[u].size();
        for(int i=0; i<len; i++)
        {
            int v=arc[u][i];
            if(dfn[v]==-1)//遍历与此点相连而且没遍历过的点
            {
                palm.push(Edge(u, v));//边压入栈
                tarjan(v, u);
                if(low[u]>low[v]) low[u]=low[v];//用子节点的low值更新自己的(low的定义不就是此点以及其后代所能连回的最先祖先的pre值嘛)
                if(dfn[u]<=low[v])//存在子节点连不回此点之前的点则这个点是割顶 d=====( ̄▽ ̄*)b --定理
                {
                    for(Edge temp; !palm.empty(); )
                    {
                        temp=palm.top();//
                        if(dfn[temp.st]<dfn[v]) break;
                        block[ind].push_back(temp), palm.pop();
                    }
                    block[ind++].push_back(Edge(u, v));//最后一个压入这个序号为ind的边就是u,v
                    palm.pop();
                    if(dfn[u]<low[v]) sum1++;//作为割顶的特殊情况 如果v的后代只能连回v自己 那么构成的是桥
                }
            }
            else if(v!=pre && dfn[v]<dfn[u])
            {
                palm.push(Edge(u, v));
                if(low[u]>dfn[v]) low[u]=dfn[v];
            }
        }
    }  

    int main()
    {
        while(scanf("%d%d", &n, &m), n!=0 || m!=0)
        {
            for(int i=0; i<n; i++) arc[i].clear();
            for(int i=0, a, b; i<m; i++)
            {
                scanf("%d%d", &a, &b);
                arc[a].push_back(b);
                arc[b].push_back(a);
            }
            for(int i=0; i<n; i++) dfn[i]=-1, block[i].clear();
            while(!palm.empty()) palm.pop();
            ind=T=sum1=sum2=0;
            for(int i=0; i<n; i++) if(dfn[i]==-1) tarjan(i, -1);
            for(int i=0; i<ind; i++)
            {
                for(int j=0; j<n; j++) vs[j]=0;
                int len=(int)block[i].size(), tot=0;
                for(int j=0; j<len; j++)
                {
                    if(!vs[block[i][j].st]) vs[block[i][j].st]=1, tot++;
                    if(!vs[block[i][j].en]) vs[block[i][j].en]=1, tot++;
                }
                if(len>tot) sum2+=len;
            }
            printf("%d %d\n", sum1, sum2);
        }
        return 0;  

    }  

8、双联通分量输出桥

hdu3849By Recognizing These Guys, We Find Social Networks Useful【map+双连通分量求桥+扩栈代码】

#include<iostream>  

#include<algorithm>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#define N 10005
#define M_M 200005
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
int top,bcnt;
int stack[N],indx;
int dfn[N],low[N],cn;
map<string,int> M; //存地点对应的编号
map<int,string> MM; //存编号对应的地点
map<string,int> final; //存每条边的编号,很有用的!
struct node{
    int next,v;
    node(){};
    node(int a,int b){
        next=a,v=b;
    }
}E[M_M];
struct ans{
    string s;
    int ind;
}ret[M_M]; //存最后结果,ind拿来排序的时候用
int head[N],NE;
int n,m;
void init(){
    M.clear();
    MM.clear();
    final.clear();
    NE=0;bcnt=0;top=0;indx=0;cn=0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}
void insert(int u,int v){
    E[NE]=node(head[u],v);
    head[u]=NE++;
}  

void tarjan(int u,int pre){ //----------------------------------1
    dfn[u]=low[u]=++indx;
    stack[top++]=u;
    for(int i=head[u];i!=-1;i=E[i].next){
        int v=E[i].v;
        if(v==pre) continue;
        if(!dfn[v]){
            tarjan(v,u);
            if(low[v]<low[u])
                low[u]=low[v];
            if(low[v]>dfn[u]){               //满足割边要求
                ret[cn].s=MM[u]+' '+MM[v];
                if(!final[ret[cn].s]) //-----------------------2
                    ret[cn].s=MM[v]+' '+MM[u];
                ret[cn].ind=final[ret[cn].s];
                cn++;
            }
        }
        else if(dfn[v]<low[u])
            low[u]=dfn[v];
    }
}
bool cmp(ans x,ans y){
    return x.ind<y.ind;
}
int bin[N];
int find(int x){
    if(bin[x]==x)
        return bin[x];
    return bin[x]=find(bin[x]);
}
bool merge(int x,int y){
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy){
        bin[fx]=fy;
        return true;
    }
    return false;
}
int main(void){
    //freopen("cin.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        init();
        int ind=1;
        for(int i=0;i<=n;i++)
            bin[i]=i;
        for(int i=1;i<=m;i++){
            string u,v;
            cin>>u>>v;
            if(!M[u]){
                M[u]=ind++;
                MM[ind-1]=u;
            }
            if(!M[v]){
                M[v]=ind++;
                MM[ind-1]=v;
            }
            final[u+' '+v]=i;
            int k1=M[u],k2=M[v];
            insert(k1,k2);
            insert(k2,k1);
            if(merge(k1,k2))
                bcnt++;
        }
        if(bcnt!=n-1){
            printf("0\n");
            continue;
        }
        tarjan(1,-1);
        sort(ret,ret+cn,cmp);
        printf("%d\n",cn);
        for(int i=0;i<cn;i++)
            cout<<ret[i].s<<endl;
    }
}  

9、经典题

poj2942圆桌骑士【点双连通分量+二分图判断】

题意:n个骑士举行圆桌会议,每次圆桌至少3人参加而且是奇数,互相憎恶的不坐旁边,统计多少个骑士不可能参加任何一个会议

做法:先找出联通分量,判断每个联通分量是否是二分图,不在是二分图的点标记,其他点就是所求。注意割点的处理,每次在判断二分图之前都先为割顶染色,因为它会被染色多次。二分图的判断就是普通的染色法

#include <iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 1005
struct Edge
{
    int u,v;
};
int pre[maxn],iscut[maxn],bccno[maxn],dfs_clock,bcc_cnt;//pre[]表示开始时间 bccno[]表示某点所在集合号 bcc_cnt表示编号
vector<int>G[maxn],bcc[maxn];
stack<Edge>S;
int dfs(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;//记录访问时间
    int child=0;
    for(int i=0;i<G[u].size();i++)//遍历与u点相连接的边
    {
        int v=G[u][i];
        Edge e=(Edge){u,v};//当前的点所连接的边
        if(!pre[v])
        {
            S.push(e);
            child++;
            int lowv=dfs(v,u);//后代
            lowu=min(lowu,lowv);//用后代的low函数更新自己
            if(lowv>=pre[u])//存在子节点连不回此点之前的点,此点是割点--定理
            {
                iscut[u]=true;//标记为割点
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                for(;;)
                {
                    Edge x=S.top();S.pop();
                    if(bccno[x.u]!=bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u]=bcc_cnt;
                    }
                    if(bccno[x.v]!=bcc_cnt)//防止加重
                    {
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v]=bcc_cnt;
                    }
                    if(x.u==u&&x.v==v) break;
                }
            }
        }
        else if(pre[v]<pre[u]&&v!=fa)//访问过v 而且v在u之前
        {
            S.push(e);
            lowu=min(lowu,pre[v]);//用反向边更新自己
        }
    }
    if(fa<0&&child==1) iscut[u]=0;//判断是根节点而且只有一个孩子那么不是割顶
    return lowu;//返回后代序号
}
void find_bcc(int n){
    memset(pre,0,sizeof(pre));
    memset(iscut,0,sizeof(iscut));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=0;
    for(int i=0;i<n;i++)
        if(!pre[i]) dfs(i,-1);
}
int odd[maxn],color[maxn];
bool bipartite(int u,int b)//判断是否是二分图
{
    for(int i=0;i<G[u].size();i++)//遍历与这个点相连接的点
    {
        int v=G[u][i];
        if(bccno[v]!=b) continue;//不在同一个连通分量 跳过
        if(color[v]==color[u]) return false;
        if(!color[v])//此点未遍历过
        {
            color[v]=3-color[u];//此点颜色等于三减去与此点相连点的颜色
            if(!bipartite(v,b)) return false;
        }
    }
    return true;
}
int A[maxn][maxn];
int main()
{
    int kase=0,n,m;
    while(scanf("%d%d",&n,&m)==2&&n)
    {
        for(int i=0;i<n;i++) G[i].clear();
        memset(A,0,sizeof(A));
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u--;v--;
            A[u][v]=A[v][u]=1;
        }
        for(int u=0;u<n;u++)
        {
            for(int v=u+1;v<n;v++)
                if(!A[u][v])
                {
                    G[u].push_back(v);
                    G[v].push_back(u);
                }
        }
        find_bcc(n);
        memset(odd,0,sizeof(odd));
        //题目要求不在任何一个简单奇圈上的节点个数
        for(int i=1;i<=bcc_cnt;i++)
        {
            memset(color,0,sizeof(color));
            for(int j=0;j<bcc[i].size();j++)
                bccno[bcc[i][j]]=i;//主要处理割顶
            int u=bcc[i][0];
            color[u]=1;//u是这个连通分量的第一个点
            if(!bipartite(u,i))//如果某个连通分量不是二分图
                for(int j=0;j<bcc[i].size();j++)
                    odd[bcc[i][j]]=1;//给其中所有节点标记在奇圈上
        }
        int ans=n;
        for(int i=0;i<n;i++) if(odd[i]) ans--;
        printf("%d\n",ans);
    }
    return 0;
}  

10、

poj3177Redundant Paths【构造双连通分量:并查集缩点 模板】

加几条边可以构成双连通分量?构造双连通分量的加边数=(原图的叶节点数+1)/2,先用并查集缩点,把所有当前的双连通分量都缩到一起,然后就构成了只有桥的图,枚举每个桥,记录每个点的次数,每次加一。只有1的点就是原图的叶结点

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <stack>  

    using namespace std;  

    const int N=5006;
    vector<int>G[N];
    struct bridge
    {
        int u,v;
    }bg[2*N];
    int vis[N],low[N],dfn[N],Time;
    int fa[N],deg[N];
    int n,m,cnt;
    void init()
    {
        for(int i=0;i<n;i++) G[i].clear();
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(vis,0,sizeof(vis));
        memset(deg,0,sizeof(deg));
        for(int i=1;i<=n;i++) fa[i]=i;
        cnt=Time=0;
    }
    int findset(int x)
    {
        if(x!=fa[x])
            fa[x]=findset(fa[x]);
        return fa[x];
    }
    void Tarjan(int u,int father)
    {
        low[u] = dfn[u] = ++Time;
        vis[u] = 1;
        for(int i=0;i<G[u].size();i++)
        {
            int v = G[u][i];
            if(v == father)
                continue;
            if(!vis[v])
            {
                Tarjan(v,u);
                low[u] = min(low[u],low[v]);
                if(low[v] > dfn[u])        //u->v为桥
                    bg[cnt].u = u,bg[cnt++].v = v;
                else   //否则,u,v同属一个连通分量,合并
                {
                    int fx = findset(u);
                    int fy = findset(v);
                    if(fx != fy)
                        fa[fx] = fy;
                }
            }
            else
                low[u] = min(low[u],dfn[v]);
        }
    }  

    int main()
    {
       // freopen("cin.txt","r",stdin);
        while(~scanf("%d%d", &n, &m))
        {
            init();
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
            Tarjan(1,-1);
            for(int i=0;i<cnt;i++)
            {
                int fx=findset(bg[i].u);
                int fy=findset(bg[i].v);
                deg[fx]++;
                deg[fy]++;
            }
            int leaf=0;
            for(int i=1;i<=n;i++)
                if(deg[i]==1)
                    leaf++;
            printf("%d\n",(leaf+1)/2);
        }
        return 0;  

    }

11、改造双向联通图成为单向联通图

poj1515Street Directions【无向图->有向图 链式前向星版tarjan求桥】

如果有环的比如a->b->c->a则一定可以变为有向,方向就是搜索的方向:a->b,b->c,c->a

即:在同一个双联通分量的边输出单向,桥输出双向

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <stack>  

    using namespace std;  

    const int MAXN=1111;  

    struct Edge
    {
        int v,next;
        bool vis;
    }edge[MAXN*MAXN];
    int n,m,NE;
    int head[MAXN];  

    void Insert(int u,int v)
    {
        edge[NE].v=v;
        edge[NE].next=head[u];
        edge[NE].vis=false;
        head[u]=NE++;
    }  

    int cnt,_count;
    int low[MAXN],dfn[MAXN],color[MAXN];
    bool mark[MAXN];
    stack<int>S;
    void Tarjan(int u,int father)
    {
        int flag=0;
        low[u]=dfn[u]=++cnt;
        mark[u]=true;
        S.push(u);
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(v==father&&!flag)
            {
                flag=1;
                continue;
            }
            if(dfn[v]==0)
            {
                Tarjan(v,u);
                low[u]=min(low[u],low[v]);
            }
            else if(mark[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            int v;
            _count++;
            do{
                v=S.top();
                S.pop();
                mark[v]=false;
                color[v]=_count;//标记每个点所在的集合序号
            }while(u!=v);
        }
    }
    void Solve(int u,int father)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(v==father) continue;
            if(color[u]==color[v]&&!edge[i].vis)
                printf("%d %d\n",u,v);//因为在同一个连通分量中 双向的边输出一个方向的就好
            else if(color[u]!=color[v]&&!edge[i].vis)
            {
                printf("%d %d\n",u,v);
                printf("%d %d\n",v,u);//因为是桥 所以输出双向的
            }
            edge[i].vis=true;
            edge[i^1].vis=true;//不管是输出了单向的还是双向的 这条边都结束了
            if(!mark[v])
            {
                mark[v]=true;
                Solve(v,u);
            }
        }
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int u,v,t=1;
        while(~scanf("%d%d",&n,&m))
        {
            if(n==0&&m==0) break;
            NE=0;
            memset(head,-1,sizeof(head));
            while(m--)
            {
                scanf("%d%d",&u,&v);
                Insert(u,v);
                Insert(v,u);  

            }
            cnt=_count=0;
            memset(dfn,0,sizeof(dfn));
            memset(mark,false,sizeof(mark));
            Tarjan(1,-1);
            printf("%d\n\n",t++);
            memset(mark,false,sizeof(mark));
            mark[1]=true;
            Solve(1,-1);
            puts("#");
        }
        return 0;
    }

12、混合图改有向图 这个题需要自己写dfs代码

poj1438One-way Traffic【双连通分量:混合图->有向图】

1) 第一次深搜时把所有有向边都当成无向边,这时候求出的桥必须双向都输出(当然了,一种给的数据一定也是双向的)

2)第二次深搜时输出这三种:

1)之前没输出过:

发现是双向的边 而且是桥 ==>输出相反的顺序(因为双连通分量必须内部相互可达,所以一定没有桥,遇到桥了,说明走反了)

发现是双向的边 而且不是桥==>就按着这个顺序顺出

2)之前遍历过:而且是双向的 直接按着这个顺序输出就好

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<cmath>
    #include<vector>
    #define inf 0x3f3f3f3f
    #define Inf 0x3FFFFFFFFFFFFFFFLL
    #define eps 1e-9
    #define pi acos(-1.0)
    using namespace std;
    typedef long long ll;
    const int maxn=2000+10;
    const int maxm=maxn*maxn;
    int pre[maxn];
    int dfs_clock,n,m;
    bool mz[maxn][maxn],flag[maxn][maxn];
    void Init()
    {
        memset(flag,0,sizeof(flag));
        memset(mz,0,sizeof(mz));
        memset(pre,0,sizeof(pre));
        dfs_clock=0;
    }
    void AddEdge(int u,int v,bool f)
    {
        mz[u][v]=true;
        flag[u][v]=f;
    }
    int Tarjan(int u,int fa)
    {
        int lowu=pre[u]=++dfs_clock;
        for(int i=1;i<=n;i++)
        {
            int v=i;
            if(!flag[u][v]||(v==fa)) continue;
            if(!pre[v])
            {
                int lowv=Tarjan(v,u);
                lowu=min(lowu,lowv);
                if(lowv>pre[u])
                {
                    printf("%d %d 2\n",u,v);
                    flag[u][v]=false;flag[v][u]=false;
                    continue;
                }
            }
            else  lowu=min(lowu,pre[v]); //之前访问过了,所以用反向边更新自己
        }
        return lowu;
    }
    int dfs(int u,int fa)
    {
        int lowu=pre[u]=++dfs_clock;
        for(int i=1;i<=n;i++)
        {
            int v=i;
            if(!flag[u][v]||(v==fa)) continue;
            if(!pre[v])
            {
                int lowv=dfs(v,u);
                lowu=min(lowu,lowv);
                if(flag[v][u])
                {
                    if(lowv>pre[u])
                    {
                        printf("%d %d 1\n",v,u);
                        flag[u][v]=false;flag[v][u]=false;  

                    }
                    else
                    {
                        printf("%d %d 1\n",u,v);
                        flag[u][v]=false;flag[v][u]=false;
                    }
                }  

            }
            else  {
                lowu=min(lowu,pre[v]); //之前访问过了,所以用反向边更新自己
                if(flag[v][u])
                {
                    printf("%d %d 1\n",u,v);
                    flag[u][v]=false;
                }
            }
        }
        return lowu;
    }
    int main()
    {
        //freopen("cin.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int u,v,type;
        scanf("%d%d",&n,&m);
        Init();
        for(int i=0;i<m;++i)
        {
            scanf("%d%d%d",&u,&v,&type);
            AddEdge(u,v,true);
            AddEdge(v,u,type==2);
        }
        Tarjan(1,-1);
        memset(pre,0,sizeof(pre));
        dfs_clock=0;
        for(int i=1;i<=n;++i)
        {
            if(!pre[i])
                dfs(i,-1);
        }
        return 0;
    }  

13、等价性证明  强联通分量基础题

HDU 3836 - Equivalent Sets【强连通分量 基础题】

派生到我的代码片

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<stack>
    using namespace std;
    #define maxn 20000
    vector<int>G[maxn];
    int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
    stack<int>S;
    int min(int a,int b){if(a<b)return a;return b;}
    void dfs(int u)
    {
        pre[u]=lowlink[u]=++dfs_clock;
        S.push(u);
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(!pre[v])
            {
                dfs(v);
                lowlink[u]=min(lowlink[u],lowlink[v]);
            }
            else if(!sccno[v])
                lowlink[u]=min(lowlink[u],pre[v]);
        }
        if(lowlink[u]==pre[u])
        {
            scc_cnt++;
            for(;;)
            {
                int x=S.top();S.pop();
                sccno[x]=scc_cnt;
                if(x==u) break;
            }
        }
    }
    void find_scc(int n)
    {
        dfs_clock=scc_cnt=0;
        memset(sccno,0,sizeof(sccno));
        memset(pre,0,sizeof(pre));
        for(int i=0;i<n;i++)
        if(!pre[i]) dfs(i);
    }
    int in0[maxn],out0[maxn];
    int main()
    {
        int T,n,m;
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0;i<n;i++) G[i].clear();
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                u--;v--;
                G[u].push_back(v);
            }
            find_scc(n);
            for(int i=1;i<=scc_cnt;i++)in0[i]=out0[i]=1;
            for(int u=0;u<n;u++)
            {
                for(int i=0;i<G[u].size();i++)
                {
                    int v=G[u][i];
                    if(sccno[u]!=sccno[v])in0[sccno[v]]=out0[sccno[u]]=0;
                }
            }
            int a=0,b=0;
            for(int i=1;i<=scc_cnt;i++)
            {
                if(in0[i])a++;
                if(out0[i]) b++;
            }
            int ans=a;
            if(ans<b) ans=b;
            if(scc_cnt==1) ans=0;
            printf("%d\n",ans);  

        }return 0;
    }

14

hdu4635Strongly connected 【求最多加多少边仍不是强连通分量】

对于一个有n个节点的完全图而言,他有n*n个边,对于这个图而言,最终的结果必然是只有两个集合(即缩点之后的团)团内是scc无环,彼此之间只有单向的通道,设两个团的节点个数分别为x,y则最终可加的边数是n*n-n-x*y-m,找最大  正解是遍历找出入度或者出度=0的团选一个最大的。为什么不是尽量凑出一个接近n/2的团?可能是无法操作吧

    #include <iostream>
    #include<cstdio>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 200005
    vector<int>G[maxn];
    int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
    stack<int>S;
    int in0[maxn],out0[maxn];
    void dfs(int u)
    {
        pre[u]=lowlink[u]=++dfs_clock;
        S.push(u);//<span style="font-family: Arial, Helvetica, sans-serif;">//每搜索到一个点,压入栈中</span>
        for(int i=0;i<G[u].size();i++)//遍历与p相连的点
        {
            int v=G[u][i];
            if(!pre[v])//不在栈中
            {
                dfs(v);
                lowlink[u]=min(lowlink[u],lowlink[v]);
            }
            else if(!sccno[v])//在栈中
                lowlink[u]=min(lowlink[u],pre[v]);
        }
        if(lowlink[u]==pre[u])//发现一个根
        {
            scc_cnt++;
            for(;;)
            {
                int x=S.top();S.pop();//词典以上的所有点全部出栈 构成一个强连通分量
                sccno[x]=scc_cnt;//scc_cnt是强连通分量的序号
                if(x==u) break;
            }
        }
    }
    void find_scc(int n)
    {
        dfs_clock=scc_cnt=0;
        memset(sccno,0,sizeof(sccno));
        memset(pre,0,sizeof(pre));
        for(int i=0;i<n;i++) if(!pre[i]) dfs(i);
    }
    int num[100000];
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int  T,n,m,cas=1;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++) G[i].clear();
            memset(num,0,sizeof(num));
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                u--;v--;
                G[u].push_back(v);
            }
            find_scc(n);
            for(int i=1;i<=scc_cnt;i++) in0[i]=out0[i]=0;
            for(int u=0;u<n;u++)
            {
                for(int i=0;i<G[u].size();i++)
                {
                    int v=G[u][i];
                    if(sccno[u]!=sccno[v]) in0[sccno[v]]=out0[sccno[u]]=1;
                }
                num[sccno[u]]++;
            }
            int maxnum=100000,maxtot;
            for(int i=1;i<=scc_cnt;i++)
            {
                //printf("i=%d,num=%d,in=%d,out=%d\n",i,num[i],in0[i],out0[i]);
                if(in0[i]==0||0==out0[i])
                {
                    if(num[i]<maxnum)
                    {
                        maxnum=num[i];
                        maxtot=i;
                    }
                }
            }
            long long ans=n*n*1LL-n-1LL*maxnum*(n-maxnum)-m;
            if(scc_cnt!=1&&ans>=0) printf("Case %d: %lld\n",cas++,ans);
            else printf("Case %d: -1\n",cas++);
        }
        return 0;
    }

15、乱搞题

CodeForces 22E Scheme【变成强联通图至少增加多少边并输出】

我们用深搜来找环,in0[]=0相当于是最后的“尾巴”,对于这个点深搜的结果是这个环的终点,终点链接起点

    #include <iostream>
    #include<cstdio>
    #include<stack>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 200005
    vector<int>G[maxn],st,ed;
    int in0[maxn];
    int col[maxn];
    int dfs(int p)
    {
        col[p]=1;
        for(int i=0;i<G[p].size();i++)
        {
            int v=G[p][i];
            if(!col[v])return col[p]=dfs(v);
            return col[p]=p;
        }
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int  n;
       // scanf("%d",&T);
        while(~scanf("%d",&n))
       // while(T--)
        {
            //scanf("%d%d",&n,&m);
            for(int i=0;i<=n;i++) G[i].clear();
            memset(in0,0,sizeof(in0));
            memset(col,0,sizeof(col));
            for(int i=1;i<=n;i++)
            {
                int v;
                scanf("%d",&v);
                in0[v]++;
                G[i].push_back(v);
            }
            int k=0,t=0;
            for(int i=1;i<=n;i++)
            {
                if(!in0[i])
                {
                    k++;
                    st.push_back(i);
                    ed.push_back(dfs(i));
                }
            }
            t=k;
            for(int i=1;i<=n;i++)
            {
                if(!col[i])
                {
                    k++;
                    st.push_back(i);
                    ed.push_back(dfs(i));
                }
            }
            if(t==0&&k==1)k=0;
            printf("%d\n",k);
            for(int i=0;i<k;i++)
            {
                printf("%d %d\n",ed[i],st[(i+1)%k]);
            }
        }
        return 0;
    }

16

poj 1144 Network【无向图求割顶模板题】

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    #define maxn 200
    int pre[maxn],iscut[maxn],dfs_clock,low[maxn];
    vector<int>G[maxn];
    int dfs(int u,int fa)
    {
        int lowu=pre[u]=++dfs_clock;
        int child=0;
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(!pre[v])
            {
                child++;
                int lowv=dfs(v,u);
                lowu=min(lowu,lowv);
                if(lowv>=pre[u])
                    iscut[u]=true;
            }
            else if(pre[v]<pre[u]&&v!=fa)
                lowu=min(lowu,pre[v]);
        }
        if(fa<0&&child==1)iscut[u]=0;
        low[u]=lowu;
        return lowu;
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
        int n;
        while(~scanf("%d",&n)&&n)
        {
            char str[200];
            memset(pre,0,sizeof(pre));
            memset(iscut,0,sizeof(iscut));
            for(int i=1;i<=n;i++)G[i].clear();
            dfs_clock=0;
            getchar();
            while(gets(str))
            {
                if(str[0]=='0')break;
                int i,u=0,len=strlen(str);
                //printf("len=%d\n",len);
                for(i=0;i<len;i++)
                {
                    if(str[i]==' ')break;
                    u+=(str[i]-'0');
                    u*=10;
                }
                i++;
                u/=10;
               // printf("u=%d\n",u);
                int tmp=0;
                for(;i<len;i++)
                {
                    if(str[i]==' ')
                    {
                        tmp/=10;
                       // printf("tmp=%d\n",tmp);
                        G[u].push_back(tmp);
                        G[tmp].push_back(u);
                        tmp=0;
                        i++;
                    }
                    tmp+=(str[i]-'0');
                    tmp*=10;
                    if(i==len-1)
                    {
                        tmp/=10;
                        //printf("tmp=%d\n",tmp);
                        G[u].push_back(tmp);
                        G[tmp].push_back(u);
                        break;
                    }
                }
            }
            for(int i=1;i<=n;i++)
            {
                if(!pre[i])dfs(i,-1);
                //if(!G[i].empty())for(int j=0;j<G[i].size();j++)printf("i=%d,j=%d\n",i,G[i][j]);
            }
            int num=0;
            for(int i=1;i<=n;i++)
                if(iscut[i])num++;
            printf("%d\n",num);
        }
        return 0;
    }  

17 乱搞题

codeforces22c System Administrator【给定一个割顶输出边 BCC】

题意:已知点数、边数、割点的序号(一直以为是割点的个数,没法求了啊==),求是否能构成满足条件的图,输出

做法:首先要判断是否满足条件,我们需要找一个边数和点数的关系,点数的下限一定是等于边数的,点数的上限可以这么想:既然有一个点是割顶,那么把剩下的点分成两团,分别都是完全图,然后割顶是中间的“纽带”,那么想来这两团的个数相等的时候边数最多,上限可求。我们顺着这个思路就可以按顺序写出边啦~~

    #include <iostream>
    #include<cstdio>
    using namespace std;
    int n,m,v;
    int main()
    {
        scanf("%d%d%d",&n,&m,&v);
        {
            if(m<n-1||m>(n*n-3*n+4)/2)
            printf("-1\n");
            else
            {
                int la;
                for(int i=1;i<=n;i++)
                {
                    if(i!=v)
                    {
                        printf("%d %d\n",i,v);
                        la=i;
                       // break;
                    }
                }
                m-=(n-1);
                for(int i=1;i<la&&m;i++)
                {
                    for(int j=i+1;j<la&&m;j++)
                    {
                        if(i!=v&&j!=v)
                        {
                            printf("%d %d\n",i,j);
                            m--;
                        }  

                    }
                }
            }
        }
        return 0;
    }  

18

hdu2460Network【双连通分量求桥 在线求lca】

题意:给定无向图,依次加一些边,求现有的桥的个数

做法:先对原始图求桥,我最开始的思路是不仅要求桥、也要求出每个点所在的BCC序号,加入新边的时候根据缩点的结果找了多少个桥,然而并不需要缩点,缩点的话还要表示bcc之间的关系再建边,好麻烦的说。正确做法就只是用lca找新加入两点之间的路径,遇到桥就修改bool变量、桥数减一。
    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    vector<int>G[100009];
    int n,m,dfs_clock,bridgenum;
    int fa[100009],isbridge[100009],pre[100009];
    bool mark[100009];
    void init()
    {
        for(int i=0;i<=n;i++) G[i].clear();
        bridgenum=dfs_clock=0;
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n;i++)fa[i]=i;
        memset(isbridge,0,sizeof(isbridge));
    }
    int dfs(int u,int father)
    {
        int lowu=pre[u]=++dfs_clock;
       // printf("u=%d,pre=%d,fa=%d\n",u,pre[u],father);
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(!pre[v])
            {
                fa[v]=u;
                int lowv=dfs(v,u);
                lowu=min(lowu,lowv);
                if(lowv>pre[u])
                {
                    isbridge[v]=true;
                    bridgenum++;
                }
            }
            else if(pre[v]<pre[u]&&v!=father)
                lowu=min(lowu,pre[v]);
        }
        return lowu;
    }
    void lca(int u,int v)
    {
        while(pre[u]>pre[v])
        {
            if(isbridge[u])
            {
                isbridge[u]=0;
                bridgenum--;
            }
            u=fa[u];
           // printf("u=%d,bridge=%d\n",u,bridgenum);
        }
        while(pre[u]<pre[v])
        {
            if(isbridge[v])
            {
                isbridge[v]=0;
                bridgenum--;
            }
            v=fa[v];
           // printf("v=%d,bridge=%d\n",v,bridgenum);
        }
       while(u!=v)
        {
            if(isbridge[u])
            {
                isbridge[u]=0;
                bridgenum--;
            }
            if(isbridge[v])
            {
                isbridge[v]=0;
                bridgenum--;
            }
            u=fa[u];v=fa[v];
        }
    }
    int main()
    {
       // freopen("cin.txt","r",stdin);
       // freopen("out.txt","w",stdout);
        int cas=1,qq;
        while(~scanf("%d%d",&n,&m))
        {
            if(n==0&&m==0)break;
            init();
            while(m--)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
            dfs(1,-1);
            printf("Case %d:\n",cas++);
            scanf("%d",&qq);
            while(qq--)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                lca(u,v);
                printf("%d\n",bridgenum);
            }
            puts("");
        }
        return 0;
    }  

19

hdu4587TWO NODES【割点】

题意:已知无向图选两个点点从原图中删掉,剩下的最大连通分量个数?

两次都是遍历所有点,找一个数组用来储存所有点的子树个数(做法类似于寻找割点),遍历第一次计算剩余的团的个数,遍历第二次寻找删除的第二个点的子树最大值 ,求和

    #pragma comment(linker, "/STACK:102400000000,102400000000")
    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    #define maxn 5009
    vector<int>G[maxn];
    int pre[maxn],dfs_cnt,iscut[maxn];
    int n,m,none;
    void init()
    {
        for(int i=0;i<n;i++)G[i].clear();
        none=n;
    }
    int dfs(int u,int fa)
    {
        int lowu=pre[u]=++dfs_cnt;
        int child=0;
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(v==none) continue;
            if(!pre[v])
            {
                child++;
                int lowv=dfs(v,u);
                lowu=min(lowu,lowv);
                if(lowv>=pre[u])
                    iscut[u]++;
            }
            else if(pre[v]<pre[u]&&v!=fa)
                lowu=min(lowu,pre[v]);
        }
        if(fa<0&&child==1)iscut[u]=0;
        return lowu;
    }
    int solve(int x)
    {
        int ans=0,left=0;
        memset(iscut,0,sizeof(iscut));
        dfs_cnt=0;
        memset(pre,0,sizeof(pre));
        none=x;
        for(int i=0;i<n;i++)
            if(i!=x&&!pre[i])
                iscut[i]--,left++,dfs(i,-1);
        for(int i=0;i<n;i++)
            if(i!=x)
            ans=max(ans,iscut[i]+1);
        ans+=left-1;
        return ans;
    }
    int main()
    {
      //  freopen("cin.txt","r",stdin);
        while(~scanf("%d%d",&n,&m))
        {
            init();
            while(m--)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
            int ans=0;
            for(int i=0;i<n;i++)
                ans=max(ans,solve(i));
            printf("%d\n",ans);
        }
        return 0;
    }  
时间: 2024-08-01 10:19:08

连通性总结的相关文章

hihocoder #1190 : 连通性&#183;四 点双联通分量

http://hihocoder.com/problemset/problem/1190?sid=1051696 先抄袭一下 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho从约翰家回到学校时,网络所的老师又找到了小Hi和小Ho. 老师告诉小Hi和小Ho:之前的分组出了点问题,当服务器(上次是连接)发生宕机的时候,在同一组的服务器有可能连接不上,所以他们希望重新进行一次分组.这一次老师希望对连接进行分组,并把一个组内的所有连接关联的服务器也视为这个组内

天梯赛 L2-013. 红色警报 图的连通性

L2-013. 红色警报 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 战争中保持各个城市间的连通性非常重要.本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报.注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报. 输入格式: 输入在第一行给出两个整数N(0 < N <=500)和M(<=5000),分别为城市个数(

Java测试网络连通性

第一种方式:利用java运行时: Java代码   /** * test network * @param ip */ private void getNetworkState(String ip) { Runtime runtime = Runtime.getRuntime(); try { log.info("=================正在测试网络连通性ip:"+ip); Process process = runtime.exec("ping " +i

第48套题【tarjan】【图&amp;树的连通性】【并查集】

Problem 1 图的连通性??题目背景??琼和雪不知从什么时候就开始形影不离得呆在一起,无话不说了那天她们在谈论图论??题意描述??“有一个无向图,每次断掉一条边,并询问两个点时候联通,你会维护么?” 琼很认真地问.“为什么要知道这个呢?”“我们总要知道自己是否身陷囹囵……你必须立刻告诉我答案哦”??输入格式??测试数据的第一行是三个正整数n.m.t, 表示无向图有n 个点,m 条边和t 个操作接下来m 行,每行两个正整数x.y,表示x 和y 之间有一条边(允许存在重边)接下来t 行,每行三

POJ2513(字典树+图的连通性判断)

//用map映射TLE,字典树就AC了#include"cstdio" #include"set" using namespace std; const int MAXN=510005; const int N=26;//26个小写英文字母 struct node{ int val;//存放字符串的hash值 node* next[N]; }; node memory[MAXN]; int ant; node* root; node* create_tree() {

hihoCoder_#1184_连通性二&#183;边的双连通分量

#1184 : 连通性二·边的双连通分量 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 在基本的网络搭建完成后,学校为了方便管理还需要对所有的服务器进行编组,网络所的老师找到了小Hi和小Ho,希望他俩帮忙. 老师告诉小Hi和小Ho:根据现在网络的情况,我们要将服务器进行分组,对于同一个组的服务器,应当满足:当组内任意一个连接断开之后,不会影响组内服务器的连通性.在满足以上条件下,每个组内的服务器数量越多越好. 比如下面这个例子,一共有6个服务器和7条连接: 其中包

Windows批处理:自动检查网络连通性

检测网络连通性我用的是丛远到近的方法,即"外网--网关--内网--本机",脚本的实现也是根据这个顺序用ping来检测,为提高检测速度,这里我只ping了2次,各位可以根据自己的需要进行修改. 使用方法大神们都会的... 复制代码,另存为.bat文件后执行. @echo off color 2F title 网络连通性检测 echo. echo. ping -n 2 223.5.5.5>%temp%\1.ping & ping -n 2 223.6.6.6>>%

Windows批处理:自动检查服务器连通性

该技术与上一篇<自动检查网络连通性>的实现原理相同,我将脚本稍微改动了下,用于检查公司服务器的连通性,简单快捷.在这里附上修改方法. @echo off color 1F title 服务器连通检测 echo. ping -n 2 192.168.1.11>%temp%\1.ping ::ping的地址修改后,每添加一个条目,临时文件1.ping需改名,如2.ping findstr "TTL" %temp%\1.ping>nul ::每添加一个条目,临时文件1

49. 蛤蟆的数据结构笔记之四十九图的连通性问题

49. 蛤蟆的数据结构笔记之四十九图的连通性问题 本篇名言:"我们不得不饮食.睡眠.游惰.恋爱,也就是说,我们不得不接触生活中最甜蜜的事情:不过我们必须不屈服于这些事物 .....--约里奥?居里"     此篇就作为数据结构入门笔记的最后一篇吧. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47135259 设图G=(V,E)是一个无向图,G的一个连通分支是一个最大的连通子图,即一个连通分支是不包含在任何更大的

[hihoCoder] 第五十二周: 连通性&#183;一

题目1 : 连通性·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失.为了避免再次出现这样的情况,学校决定对校园网络进行重新设计. 学校现在一共拥有N台服务器(编号1..N)以及M条连接,保证了任意两台服务器之间都能够通过连接直接或者间接的数据通讯. 当发生黑客攻击时,学校会立刻切断网络中的一条连接或是立刻关闭一台服务器,使得整个网络被隔离成两个独立的部分. 举个例子,对于以下