[HAOI2006]受欢迎的牛

解题报告

原题见COGS tarjan的典型题、入门题,真的是裸的tarjan。 这道题就是先搜索强连通,再统计强连通的出度,如果有两个或以上出度为零的强连通,则此题无解。 最后输出唯一的强连通的大小,也就是它内部的点的个数。 代码推荐以新页面打开,因为有些注释打的有点长。。//话说这是我第一次 写注释把注释打这么详细啊

#include 
#include 
#include 
const int MX = 10005;
using namespace std;
int low[MX], dfn[MX], cnt[MX], n, m, be[MX], sccn;
bool out[MX];
class stack {//手写栈
private:
    int s[MX];
    int top_;
    bool instack[MX];
public:
    int top() {
        return s[top_];
    }
    void pop() {
        instack[s[top_]] = false;
        top_--;
    }
    void push(int num) {
        instack[num] = true;
        s[++top_] = num;
    }
    bool in(int num) {
        return instack[num];
    }
}s;
struct edge {
    int to, ne;
}e[MX * 5];//邻接表存边
int head[MX], tot;
void tarjan(int now) {
    int to;
    low[now] = dfn[now] = ++tot;//dfn为当前文件序号, low是一个tarjan独有的特殊数组。。。
    s.push(now);
    for (int i = head[now]; i; i = e[i].ne) {
        to = e[i].to;
        if (!dfn[to]) {//如果之前没有访问过该节点
            tarjan(to);//循环进行深搜
            low[now] = min(low[to], low[now]);//更新low值
        }
        else if (s.in(to)) {//已经访问过该节点,并且是在这次深搜中访问的,表明有环
            low[now] = min(low[to], low[now]);//更新low值
        }
    }
    if (dfn[now] == low[now]) {//进行缩点
            int tmp;
            sccn++;//强连通的编号
            do {
                tmp = s.top();
                s.pop();
                cnt[sccn]++;
                be[tmp] = sccn;//tmp属于的强连通
            }while (tmp != now);
    }
}
void addedge(int x0, int y0) {//邻接表加边
    tot++;
    e[tot].to = y0;
    e[tot].ne = head[x0];
    head[x0] = tot;
}
int get_num() {//日常读入优化
    int ans = 0;
    char tmp;
    tmp = getchar();
    while (tmp > '9' || tmp < '0')    tmp = getchar();
    while (tmp = '0') {
        ans = ans * 10 + tmp - 48;
        tmp = getchar();
    }
    return ans;
}
int Main() {
    //freopen("cow.in", "r", stdin);
    //freopen("cow.out", "w", stdout);
    n = get_num();
    m = get_num();
    int x, y;
    for (int i = 1; i <= m; i++) {
        x = get_num();
        y = get_num();
        addedge(x, y);
    }
    for (int i = 1; i <= n; i++) {
        if(!dfn[i]) tarjan(i);//如果该节点并没有被访问过,进行深搜遍历
    }
    for (int i = 1; i <= n; i++) {
        for (int j = head[i]; j; j = e[j].ne) {
            if (be[i] != be[e[j].to]) {//如果该节点和它指向的节点不属于同一个强连通
                out[be[i]] = true;//就说明这个强连通有出度
                break;//然后证明这个节点不是我们要找的答案,可以直接遍历下一个节点
            }
        }
    }
    int tmp = 0;
    int ans = 0;
    for (int i = 1; i <= sccn; i++) {
        if (!out[i]) {//如果这个节点没有出度
            ans = cnt[i];//更新答案
            tmp++;//没有出度的强连通的个数
        }
        if (tmp == 2) {//如果有一个以上的没有出度的强连通,则此题无解
            cout << "0" << endl;
            return 0;
        }
    }
    cout << ans << endl;
    return 0;
}
int AA = Main();//对于COGS的一点神奇的优化,至今不明原理
int main() {;}

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理