Tarjan强连通分量SCC分解+缩点 两水题连发~ (BZOJ 1654 && 1051)

闲着无聊没事干,水两道辣鸡SB题。。。这种tarjan就直接vector秒杀了。。。(洛谷上也都有貌似)

1654: [Usaco2006 Jan]The Cow Prom 奶牛舞会

Time Limit: 5 Sec  Memory Limit: 64 MB

Description

    约翰的N(2≤N≤10000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别上鲜花,她们要表演圆舞.
    只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好,顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛.为了跳这种圆舞,她们找了M(2≤M≤50000)条绳索.若干只奶牛的蹄上握着绳索的一端,绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索,找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终能回到贝茜,则她的圆舞跳得成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圜舞.如果这样的检验无法完成,那她的圆舞是不成功的.
    如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组合.
    给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合?

Input

    第1行输入N和M,接下来M行每行两个整数A和B,表示A牵引着B.

Output

    成功跳圆舞的奶牛组合数.

Sample Input

5 4
2 4
3 5
1 2
4 1

INPUT DETAILS:

ASCII art for Round Dancing is challenging. Nevertheless, here is a
representation of the cows around the stock tank:
_1___
/**** \
5 /****** 2
/ /**TANK**|
\ \********/
\ \******/ 3
\ 4____/ /
\_______/

Sample Output

1

HINT

1,2,4这三只奶牛同属一个成功跳了圆舞的组合.而3,5两只奶牛没有跳成功的圆舞

Source

Silver

这题就不要缩点,裸的模板。。。无语。。。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
 
struct node
{
    int num,par;
}p[10005];
int n,m,index=1,x,y,cnt;
vector<int>v[10005];
bool vis[10005];
int ind[10005];
stack<int>s;
bool in_stack[10005];
 
void tarjan(int x)
{
    p[x].num=index;
    p[x].par=p[x].num;
    index++;
    vis[x]=1;
    in_stack[x]=1;
    s.push(x);
     
    for(int i=0;i<v[x].size();i++)
    {
        if(!vis[v[x][i]])
        {
            tarjan(v[x][i]);
            p[x].par=min(p[x].par,p[v[x][i]].par);
        }
        else if(in_stack[v[x][i]])
        {
            p[x].par=min(p[x].par,p[v[x][i]].num);
        }
    }
     
    if(p[x].num==p[x].par)
    {
        ++cnt;
        int gutc=0;
        int k;
        do
        {
            k=s.top();
            s.pop();
            in_stack[k]=0;
            gutc++;
        }while(k!=x);
        if(gutc==1)cnt--;
    }
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
    }
     
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])tarjan(i);
    }
     
    printf("%d\n",cnt);
    return 0;
} 

  

1051: [HAOI2006]受欢迎的牛

Time Limit: 10 Sec  Memory Limit: 162 MB

Description

  每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这
种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头
牛被所有的牛认为是受欢迎的。

Input

  第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可
能出现多个A,B)

Output

  一个数,即有多少头牛被所有的牛认为是受欢迎的。

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

HINT

100%的数据N<=10000,M<=50000
 
这题要缩点的思路,但不需要真正意义上的缩圈成点。
具体是找出SCC分解缩点,然后对于重构的图,出度为0的点若不是有且仅有一个,就说明没有最受欢迎的牛,反之就是此强连通分量里节点个数为ans。
自己体会下,无脑题。
 
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
 
struct node
{
    int num,par;
}p[50005];
int n,m,index=1,x,y,cnt,ans;
vector<int>v[50005];
bool vis[50005];
int ind[50005];
stack<int>s;
bool in_stack[50005];
int belong[50005],sum[50005],cntx[50005];
 
void tarjan(int x)
{
    p[x].num=index;
    p[x].par=p[x].num;
    index++;
    vis[x]=1;
    in_stack[x]=1;
    s.push(x);
     
    for(int i=0;i<v[x].size();i++)
    {
        if(!vis[v[x][i]])
        {
            tarjan(v[x][i]);
            p[x].par=min(p[x].par,p[v[x][i]].par);
        }
        else if(in_stack[v[x][i]])
        {
            p[x].par=min(p[x].par,p[v[x][i]].num);
        }
    }
     
    if(p[x].num==p[x].par)
    {
        ++cnt;
        int k;
        do
        {
            k=s.top();
            s.pop();
            belong[k]=cnt;
            cntx[cnt]++;
            in_stack[k]=0;
        }while(k!=x);
    }
}
 
int main()
{   
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
    }
     
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])tarjan(i);
    }
     
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<v[i].size();j++)
        {
            if(belong[v[i][j]]!=belong[i])
            {
                sum[belong[i]]++;
            }
        }
    }
     
    int kkk=0;
    for(int i=1;i<=cnt;i++)
    {
        if(!sum[i])
        {
            kkk++;
            ans=cntx[i];
        }
    }
    if(kkk!=1)printf("%d\n",0); else printf("%d\n",ans);
    return 0;
} 

滚去复习文化课期中考试了。现在感觉越来越懒得写博客发题解了,要写也都写水无脑题。。。。。。

转载于:https://www.cnblogs.com/winmt/p/6709553.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值