[HNOI2012]矿场搭建 (点双连通)

题目

[HNOI2012]矿场搭建

解析

这个题做的我十分自闭。。
没看出这个是个点双,然后一晚上+半上午。。
一看肯定和割点有关,我们找到所有的点双,会发现有这么几种情况

  1. 连通块中一个割点也没有,这时我们至少要建两个出口,以防万一某个出口塌了就GG了,方案的话就从size(联通块大小)个点中随便选两个,也是\(\dbinom{size}{2}\)个。
  2. 联通块中有一个割点,如果这个割点塌了就GG了,需要一个出口,但如果塌的不是割点,我们可以通过割点跑到另一个连通块中,所以只需要割点。根据乘法原理,方案数只需要乘上这个连通块的size。
  3. 联通块中大于两个割点的时候,若一个割点塌了,可以通过另一个割点跑到另一个联通块里,所以不需要再加出口。

需要注意的是
题目中没有给出具体多少个点,但应该是连续的,因为我按连续的做的

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, m, js, cnt, num, sum, size, tot, ans1, ans2;
/*  js计数器 
    sum联通块编号
    tot联通块中割点个数
    size联通块中点的个数 
    ans1第一问答案
    ans2第二问答案 
*/
int head[N], dfn[N], low[N], bel[N];
bool cut[N], vis[N];
class node {
    public :
        int v, nx;
} e[N];

template<class T>inline void read(T &x) {
    x = 0; int f = 0; char ch = getchar();
    while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    x = f ? -x : x;
    return;
}

inline void add(int u, int v) {
    e[++num].nx = head[u], e[num].v = v, head[u] = num;
}

void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++cnt;
    int child = 0;
    for (int i = head[u]; ~i; i = e[i].nx) {
        int v = e[i].v;
        if (!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u] && u != fa) cut[u] = 1, vis[u] = 1;
            if (u == fa) child++;
        }
        low[u] = min(low[u], dfn[v]);
    }
    if (child >= 2 && u == fa) cut[u] = 1, vis[u] = 1;
}

void dfs(int u) {
    vis[u] = 1;
    size++;
    for (int i = head[u]; ~i; i = e[i].nx) {
        int v = e[i].v;
        if (!vis[v] && !cut[v]) dfs(v);     //没有被访问过且不是割点 
        if (cut[v] && bel[v] != sum) bel[v] = sum, tot++;   //用来标记割点在哪个连通块内 
    }
}

signed main() {
    while (scanf("%lld", &n) && n) {
        num = cnt = m = ans1 = 0;
        ans2 = 1;
        memset(e, 0, sizeof (e));
        memset(dfn, 0, sizeof (dfn));
        memset(low, 0, sizeof (low));
        memset(cut, 0, sizeof (cut));
        memset(vis, 0, sizeof (vis));
        memset(bel, 0, sizeof (bel));
        memset(head, -1, sizeof (head));
        for (int i = 1, x, y; i <= n; ++i) {
            read(x), read(y);
            add(x, y), add(y, x);
            m = max(m, max(x, y));
        }
        for (int i = 1; i <= m; ++i) if (!dfn[i]) tarjan(i, i);
        for (int i = 1; i <= m; ++i) {
            if (vis[i] || cut[i]) continue;
            tot = size = 0;
            sum++;
            dfs(i);
            if (tot == 0) ans1 += 2, ans2 *= ((size - 1) * size) >> 1;
            if (tot == 1) ans1++, ans2 *= size;
        }
        printf("Case %lld: %lld %lld\n", ++js, ans1, ans2);
    }
}

转载于:https://www.cnblogs.com/lykkk/p/10921489.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值