zoj 3378 Attack the NEET Princess 求1~n路径上的必经之路






Fujiwara no Mokou (藤原妹紅) detests Kaguya, and has been planing to attack her for a long time. But she nerver got the chance before because Kaguya kept staying in Eientei, under the protection of Yagokoro Eirin (八意永琳). Knowing that Kaguya will go outside today, Mokou decides to wait on certain road and attack her once she passes there. Mokou don't know which path Kaguya will choose, but she thinks that there always exits some roads where she can always meet Kaguya.
Input


There are multiple cases. Each case begins with two integers 2 ≤ n ≤ 10000 -- the number of villages and 2 ≤ m ≤ 100000 -- the number of roads. Then m lines, each contains two integers a and b(0 ≤ a, b < n), indicating a road connecting village a and village b. Village 0 is Eientei and Village n-1 is Hakurei Shrine. They are always connected by roads. There may be more than one roads between two villages.
Output


Find out the roads where Mokou can always meet Kaguya. For each case, output the number of roads in the first line, then output the numbers of the roads in ascending order in the second line.
Sample Input


3 2
0 1
1 2


7 8
0 1
0 2
1 3
2 3
3 4
3 5
4 6
5 6


Sample Output


2
0 1
0









//自己模板
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=150000;
//无向联通图求割边  前提:没有重边
int hash[maxn];
int repeat[maxn];
int ind[maxn];
int isbridge[maxn];
void init_hash()
{
    memset(hash,-1,sizeof(hash));
    memset(repeat,0,sizeof(repeat));
    memset(ind,0,sizeof(ind));
    memset(isbridge,0,sizeof(isbridge));
}
int ishash(int u,int t,int index)//true 表示已经存在
{
    int tmp=u*10000+t;
    int ret=tmp%maxn;
    while(hash[ret]!=-1&&hash[ret]!=tmp)
    {
        ret=(ret+1)%maxn;
    }
    if(hash[ret]==tmp) return ind[ret];
    hash[ret]=tmp;
    ind[ret]=index;
    return -1;
}
struct edge
{
    int t,index;
    int next;
};
int V,E;
int p[maxn];
edge G[maxn*3];
int l;
void init()
{
    memset(p,-1,sizeof(p));
    l=0;
}
void addedge(int u,int t,int index,int l)
{
    G[l].t=t;
    G[l].index=index;
    G[l].next=p[u];
    p[u]=l;
}
//tarjan 求割点 割边
int cut[maxn];//cut[i]非0表示i是割点
int color[maxn];//颜色:0表示没有访问,1表示正在访问,2表示访问结束
int lowc[maxn];//表示i及i的子孙相连的辈分最高的祖先节点所在的深度
int d[maxn];//表示i节点在树中的深度
int root;//根节点
int fath;//父节点
int pcnt;//割点个数
int egcnt;//割边个数
int egt[maxn];
void dfs(int u,int fath,int deep)
{
    color[u]=1;//正在访问
    lowc[u]=d[u]=deep;//深度
    int tot=0;//子树个数
    for(int i=p[u];i!=-1;i=G[i].next)
    {
        int t=G[i].t;
        int index=G[i].index;
        if(t!=fath&&color[t]==1)
        {
            lowc[u]=min(lowc[u],d[t]);
        }
        if(color[t]==0)
        {
            dfs(t,u,deep+1);
            tot++;//子树加1
            lowc[u]=min(lowc[u],lowc[t]);
            //求割点
            //if((u==root&&tot>1)||(u!=root&&lowc[t]>=d[u])) cut[u]=1;//不能将pscnt++写到这里
            //求割边
            if(lowc[t]>d[u]) //edge[u][t]=true;  u->t是割边
            {
                //判断重边
                if(!repeat[index]) egt[egcnt++]=index,isbridge[index]=1;
            }
        }
    }
    color[u]=2;
}
void calc()
{
    pcnt=egcnt=0;
    memset(cut,0,sizeof(cut));
    memset(color,0,sizeof(color));
    memset(lowc,0,sizeof(lowc));
    memset(d,0,sizeof(d));
    root=1;
    dfs(root,-1,1);
    sort(egt,egt+egcnt);
    //for(int i=1;i<=V;i++) if(cut[i]) pcnt++;
}




int num;
int path[maxn];
int vis[maxn];
bool fdfs(int u)
{
    if(u==V)
    {
        return true;
    }
    vis[u]=1;
    for(int i=p[u];i!=-1;i=G[i].next)
    {
        int t=G[i].t,index=G[i].index;
        if(!vis[t])
        {
            if(fdfs(t))
            {
                if(isbridge[index]) path[num++]=index;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    while(scanf("%d%d",&V,&E)==2)
    {
        init();
        init_hash();
        for(int i=1;i<=E;i++)
        {
            int u,t,index=i;
            scanf("%d%d",&u,&t);u++,t++;
            if(u>t) swap(u,t);
            int ret;
            if((ret=ishash(u,t,i))!=-1)
            {
                repeat[ret]=1;
                continue;
            }
            addedge(u,t,index,l++);
            addedge(t,u,index,l++);
        }
        calc();
       /** printf("%d\n",egcnt);
        for(int i=0;i<egcnt;i++)
        {
            printf("%d",egt[i]);
            if(i!=egcnt-1) printf(" ");
            else printf("\n");
        }*/
        //求1~V上的必经之路 fdfs一次,求出经过的桥
        num=0;
        memset(vis,0,sizeof(vis));
        fdfs(1);
        sort(path,path+num);
        if(num==0) printf("%d\n\n",num);
        else
        {
            printf("%d\n", num);
            printf("%d", path[0]-1);
            for (int i = 1; i < num; i ++ )
                printf(" %d", path[i]-1);
            printf("\n");
        }
    }
    return 0;
}
//网上代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;


const int N = 10000 + 3;


struct P
{
    int ed;
    int id;
}gp;


int n, m;
int root;
int ancestor[N];
int d[N];
int visited[N];  // -1表示未被遍历,0表示正在被遍历还没遍历完, 1表示已经完全遍历
int father[N];
int bridgeN;  // 桥数
vector <int> edge[N];
vector <int> bridge[N];
vector <P> hash[N];
vector <int> ans;
int mark[100000 + 3];


void init()
{
    bridgeN = 0;
    for (int i = 1; i <= n; i++)
    {
        edge[i].clear();
        bridge[i].clear();
        hash[i].clear();
    }
    memset(visited, -1, sizeof(visited));
    memset(father, -1, sizeof(father));
}


void addEdge(int u, int v)
{
    edge[u].push_back(v);
    edge[v].push_back(u);
}


void dfs(int node, int deep)
{
    visited[node] = 0;
    d[node] = deep;
    ancestor[node] = deep;
    int tot = 0;  // 子结点数


    int i, u, size = edge[node].size();


    for (i = 0; i < size; i++)
    {
        u = edge[node][i];
        if (visited[u] == -1)
        {
            father[u] = node;
            dfs(u, deep + i + 1);
            tot++;
            ancestor[node] = min(ancestor[node], ancestor[u]);




            // 桥判断
            if (ancestor[u] > d[node])
            {
                bridgeN++;
                bridge[node].push_back(u);
                bridge[u].push_back(node);
            }
        }
        else if (visited[u] == 0)
        {
            if (u != father[node])
                ancestor[node] = min(ancestor[node], ancestor[u]);
        }
    }
    visited[node] = 1;
}


bool ddfs(int node)
{
    if (node == n)
        return true;


    visited[node] = 1;
    int i, u, size = edge[node].size(), j, len = hash[node].size();;
    for (i = 0; i < size; i++)
    {
        u = edge[node][i];
        if (visited[u] == -1)
        {
            if (ddfs(u))
            {
                for (j = 0; j < len; j++)
                    if (hash[node][j].ed == u)
                    {
                        if (mark[hash[node][j].id] == 0)
                            mark[hash[node][j].id] = 2;
                        break;
                    }
                return true;
            }
        }
    }
    return false;
}


void bridgeOutput()
{
    ans.clear();


    memset(visited, -1, sizeof(visited));
    ddfs(1);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < bridge[i].size(); j++)
        {
            int size = hash[i].size();
            for (int k = 0; k < size; k++)
            {
                if (hash[i][k].ed == bridge[i][j])
                {
                    if (mark[hash[i][k].id] == 2)
                        ans.push_back(hash[i][k].id);
                    break;
                }
            }
        }
    }
}


bool cmp(int a, int b)
{
    return a < b ? 1 : 0;
}


int main ()
{
    int i, j, u, v, en;
    int size;
    while (scanf("%d%d", &n, &m) != EOF)
    {
        init();
        en = 0;
        memset(mark, 0, sizeof(mark));


        for (i = 1; i <= m; i++)
        {
            scanf("%d%d", &u, &v);
            addEdge(u + 1, v + 1);
            size = hash[u + 1].size();
            for (j = 0; j < size; j++)
                if (hash[u + 1][j].ed == v + 1)
                {
                    mark[hash[u + 1][j].id] = 1;
                    break;
                }
            if (j >= size)
            {
                gp.ed = v + 1;
                gp.id = en;
                hash[u + 1].push_back(gp);


                gp.ed = u + 1;
                gp.id = en;
                hash[v + 1].push_back(gp);
            }
            en++;//?
        }


        for (i = 1; i <= n; i++)
            if (visited[i] == -1)
            {
                root = i;
                dfs(root, 1);     // dfs找桥和割点
            }


        bridgeOutput();
        size = ans.size();
        if (size < 2)
            printf("%d/n/n", size);
        else
        {
            printf("%d/n", size / 2);
            sort(ans.begin(), ans.end(), cmp);
            printf("%d", ans[0]);
            for ( i = 2; i < size; i += 2 )
                printf(" %d", ans[i]);
            printf("/n");
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值