图的联通入门题

BZOJ 1051 [HAOI2006]受欢迎的牛

题意:每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。
   你的任务是求出有多少头、牛被所有的牛认为是受欢迎的。
题解:强连通分量求缩点重构图(必定是DAG),出度为0的点为一个的时候输出该分量的大小,否则不存在。
#include <bits/stdc++.h>

const int maxn=5e4+5;

struct Edge{
    int to, next;
}edge[maxn], redge[maxn];
int head[maxn], tot;
int rhead[maxn], cnt;

int low[maxn], dfn[maxn];
int stk[maxn], instk[maxn], belong[maxn], num[maxn];
int idx, top, scc;
int n, m;

void addedge(int u, int v)
{
    edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;
}

void tarjan(int u)
{
    int v;
    low[u]=dfn[u]=++idx;
    stk[top++]=u, instk[u]=true;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        v=edge[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=std::min(low[u], low[v]);
        }
        else if(instk[v]){
            low[u]=std::min(low[u], dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        scc++;
        do{
            v=stk[--top];
            instk[v]=false;
            belong[v]=scc;
            num[scc]++;
        }while(v!=u);
    }
}

void rebuild()
{
    for(int u=1; u<=n; u++)
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].to;
            if(belong[u]!=belong[v])
            {
                redge[cnt].to=belong[v];
                redge[cnt].next=rhead[belong[u]];
                rhead[belong[u]]=cnt++;
            }
        }
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(head, -1, sizeof(head));
    memset(rhead, -1, sizeof(rhead));
    for(int i=0; i<m; i++)
    {
        int u, v; scanf("%d%d", &u, &v);
        addedge(u, v);
    }
    for(int u=1; u<=n; u++)
        if(!dfn[u]) tarjan(u);
    rebuild();
    int ok=0, ans;
    for(int u=1; u<=scc; u++)
        if(rhead[u]==-1){
            ok++; ans=num[u];
        }
    printf("%d", ok==1? ans:0);
    return 0;
}
View Code

 仅记录缩点重构图的出度

 1 #include <bits/stdc++.h>
 2 
 3 const int maxn=5e4+5;
 4 
 5 struct Edge{
 6     int to, next;
 7 }edge[maxn], redge[maxn];
 8 int head[maxn], tot;
 9 int out[maxn];
10 
11 int low[maxn], dfn[maxn];
12 int stk[maxn], instk[maxn], belong[maxn], num[maxn];
13 int idx, top, scc;
14 int n, m;
15 
16 void addedge(int u, int v)
17 {
18     edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;
19 }
20 
21 void tarjan(int u)
22 {
23     int v;
24     low[u]=dfn[u]=++idx;
25     stk[top++]=u, instk[u]=true;
26     for(int i=head[u]; i!=-1; i=edge[i].next)
27     {
28         v=edge[i].to;
29         if(!dfn[v]){
30             tarjan(v);
31             low[u]=std::min(low[u], low[v]);
32         }
33         else if(instk[v]){
34             low[u]=std::min(low[u], dfn[v]);
35         }
36     }
37     if(low[u]==dfn[u])
38     {
39         scc++;
40         do{
41             v=stk[--top];
42             instk[v]=false;
43             belong[v]=scc;
44             num[scc]++;
45         }while(v!=u);
46     }
47 }
48 
49 void rebuild()
50 {
51     for(int u=1; u<=n; u++)
52         for(int i=head[u]; i!=-1; i=edge[i].next)
53         {
54             int v=edge[i].to;
55             if(belong[u]!=belong[v])
56                 out[belong[u]]=1;
57         }
58 }
59 
60 int main()
61 {
62     scanf("%d%d", &n, &m);
63     memset(head, -1, sizeof(head));
64     for(int i=0; i<m; i++)
65     {
66         int u, v; scanf("%d%d", &u, &v);
67         addedge(u, v);
68     }
69     for(int u=1; u<=n; u++)
70         if(!dfn[u]) tarjan(u);
71     rebuild();
72     int ok=0, ans;
73     for(int u=1; u<=scc; u++)
74         if(!out[u]){
75             ok++; ans=num[u];
76         }
77     printf("%d", ok==1? ans:0);
78     return 0;
79 }
View Code

 

 BZOJ 1529 Ska Piggy Panks  https://www.luogu.org/problem/P3420

题意:Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里,他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐。给出:表示第i个存钱罐对应的钥匙放置在了第x个存钱罐中。

题解:1.tarjan缩点重构后求入度为0的点的个数,此题中会爆内存。

   2.并查集直接搞就行。

#include <bits/stdc++.h>

const int maxn=1e6+5;
int fa[maxn];

int find(int x)
{
    return fa[x]==x? x:fa[x]=find(fa[x]);
}

int main()
{
    int n;
    scanf("%d", &n);
    for(int i=1; i<=n; i++) fa[i]=i;
    for(int i=1; i<=n; i++)
    {
        int x;
        scanf("%d", &x);
        int p=find(x), q=find(i);
        if(p!=q) fa[q]=p;
    }
    int ans=0;
    for(int i=1; i<=n; i++)
        if(fa[i]==i) ans++;
    printf("%d\n", ans);
    return 0;
}
View Code

 

CF 427C  Checkposts  http://codeforces.com/problemset/problem/427/C

题意:条件:A checkpost at junction i can protect junction j if either i = j or the police patrol car can go to j from i and then come back to i.  Building checkposts costs some money

求:You have to determine the minimum possible money needed to ensure the security of all the junctions. Also you have to find the number of ways to ensure the security in minimum price and in addition in minimum number of checkposts.

题解:tarjan的时候维护num【】,minw【】即可。ans1连加,ans2连乘

#include <bits/stdc++.h>

const int maxn=3e5+5, mod=1e9+7;

struct Edge{
    int to, next;
}edge[maxn];
int head[maxn], tot;

int low[maxn], dfn[maxn];
int stk[maxn], instk[maxn], num[maxn];
int idx, top, scc;
int n, m;
int w[maxn], minw[maxn];

void addedge(int u, int v)
{
    edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;
}

void tarjan(int u)
{
    int v;
    low[u]=dfn[u]=++idx;
    stk[top++]=u, instk[u]=true;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        v=edge[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=std::min(low[u], low[v]);
        }
        else if(instk[v]){
            low[u]=std::min(low[u], dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        scc++;
        do{
            v=stk[--top];
            instk[v]=false;
            if(minw[scc]>w[v]){
                num[scc]=1; minw[scc]=w[v];
            }
            else if(minw[scc]==w[v]) num[scc]++;
        }while(v!=u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    memset(minw, 0x3f, sizeof(minw));
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &w[i]);
    scanf("%d", &m);
    for(int i=0; i<m; i++)
    {
        int u, v; scanf("%d%d", &u, &v);
        addedge(u, v);
    }
    for(int u=1; u<=n; u++)
        if(!dfn[u]) tarjan(u);
    long long ans1=0, ans2=1;
    for(int i=1; i<=scc; i++)
    {
        ans1+=minw[i];
        ans2=(ans2*num[i])%mod;
    }
    printf("%lld %lld\n", ans1, ans2);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Yokel062/p/11431747.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值