洛谷P2812/P2746解题思路

这是一道简单的强连通分量(有向图

先用链式向前星建图,然后Tarjan+缩点

看看哪些学校内部可以共享软件

以下是重点

统计有几个拓扑序入度为0的强连通分量,即为作为共享软件的母机数

有几个拓扑序出度为0的强连通分量,即至少要添加几条线路(每次将出度为0的点往入度为0的点连

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 1e4 + 5;
struct edge{
    int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx;
inline void add(int u,int v){
    e[++tot] = (edge){v,head[u]};
    head[u] = tot;
}
bool vis[N];
void tarjan(int u){
    low[u] = dfn[u] = ++cnt;
    stac[++top] = u;
    vis[u] = true;
    repe(i,u)
        if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[u] = min(low[u],low[e[i].v]);
        }
        else if(vis[e[i].v])
            low[u] = min(low[u],dfn[e[i].v]);
    if(low[u] == dfn[u]){
        int cur;
        idx++;
        do {
            cur = stac[top--];
            vis[cur] = false;
            bel[cur] = idx;
        } while(cur != u);
    }
}
int main(){
    scanf("%d",&n);
    rep(i,n){
        scanf("%d",&x);
        while(x){
            add(i,x);
            scanf("%d",&x);
        }
    }
    rep(i,n) if(!dfn[i]) tarjan(i);
    rep(i,n) repe(j,i)    if(bel[i] != bel[e[j].v])
            rudu[bel[e[j].v]]++,chudu[bel[i]]++;
    rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
    printf("%d\n%d",ans1,ans2);
    return 0;
}

代码交上去了,然后获得了——81分的高分

查缺补漏

下载数据,

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

这是一个环……出度为0的点虽然有但是……不需要加边呀

特判了,交上去72分……

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 105;
struct edge{
    int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx;
bool ccnt;
inline void add(int u,int v){
    e[++tot] = (edge){v,head[u]};
    head[u] = tot;
}
bool vis[N];
void tarjan(int u){
    low[u] = dfn[u] = ++cnt;
    stac[++top] = u;
    vis[u] = true;
    repe(i,u)
        if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[u] = min(low[u],low[e[i].v]);
        }
        else if(vis[e[i].v])
            low[u] = min(low[u],dfn[e[i].v]);
    if(low[u] == dfn[u]){
        int cur;
        idx++;
        do {
            cur = stac[top--];
            vis[cur] = false;
            bel[cur] = idx;
        } while(cur != u);
    }
}
int main(){
    scanf("%d",&n);
    rep(i,n){
        scanf("%d",&x);
        while(x){
            add(i,x);
            scanf("%d",&x);
        }
    }
    rep(i,n) if(!dfn[i]) tarjan(i);
    rep(i,n) repe(j,i)    if(bel[i] != bel[e[j].v])
            rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt = true;
    rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
    printf("%d\n%d",ans1,ccnt ? ans2 : 0);
    return 0;
}

这张图长这样

没有考虑不连通的点……(谁让我靠边判断呢

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 1e4 + 5;
struct edge{
    int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx,ccnt,num;
inline void add(int u,int v){
    e[++tot] = (edge){v,head[u]};
    head[u] = tot;
}
bool vis[N];
void tarjan(int u){
    low[u] = dfn[u] = ++cnt;
    stac[++top] = u;
    vis[u] = true;
    repe(i,u)
        if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[u] = min(low[u],low[e[i].v]);
        }
        else if(vis[e[i].v])
            low[u] = min(low[u],dfn[e[i].v]);
    if(low[u] == dfn[u]){
        int cur;
        idx++;
        do {
            cur = stac[top--];
            vis[cur] = false;
            bel[cur] = idx;
        } while(cur != u);
    }
}
int main(){
    scanf("%d",&n);
    rep(i,n){
        scanf("%d",&x);
        while(x){
            add(i,x);
            scanf("%d",&x);
        }
    }
    rep(i,n) if(!dfn[i]) tarjan(i),num++;
    rep(i,n) repe(j,i)    if(bel[i] != bel[e[j].v])
            rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt++;
    rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
    printf("%d\n%d",ans1,ccnt == 0 && num == 1 ? 0 : ans2);
    return 0;
}

已经很接近正确答案了!但是……90分

再看看~制造出一组hack!

出度为0的点有2个,但是入度为0的点有3个……

要连至少3条边才能让它变成强连通分量啊!!!

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
const int N = 1e4 + 5;
struct edge{
    int v,next;
}e[N*N];
int dfn[N],low[N],stac[N],head[N],bel[N],rudu[N],chudu[N];
int n,x,tot,cnt,ans1,ans2,top,idx,ccnt,num;
inline void add(int u,int v){
    e[++tot] = (edge){v,head[u]};
    head[u] = tot;
}
bool vis[N];
void tarjan(int u){
    low[u] = dfn[u] = ++cnt;
    stac[++top] = u;
    vis[u] = true;
    repe(i,u)
        if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[u] = min(low[u],low[e[i].v]);
        }
        else if(vis[e[i].v])
            low[u] = min(low[u],dfn[e[i].v]);
    if(low[u] == dfn[u]){
        int cur;
        idx++;
        do {
            cur = stac[top--];
            vis[cur] = false;
            bel[cur] = idx;
        } while(cur != u);
    }
}
int main(){
    scanf("%d",&n);
    rep(i,n){
        scanf("%d",&x);
        while(x){
            add(i,x);
            scanf("%d",&x);
        }
    }
    rep(i,n) if(!dfn[i]) tarjan(i),num++;
    rep(i,n) repe(j,i)    if(bel[i] != bel[e[j].v])
            rudu[bel[e[j].v]]++,chudu[bel[i]]++,ccnt++;
    rep(i,idx) ans1 += rudu[i] == 0,ans2 += chudu[i] == 0;
    printf("%d\n%d",ans1,ccnt == 0 && num == 1 ? 0 : max(ans1,ans2));
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值