题解:洛谷 P11380 排队

【题目来源】

洛谷:P11380 [GESP202412 八级] 排队

【题目描述】

小杨所在班级共有 \(n\) 位同学,依次以 \(1,2,\dots,n\) 标号。这 \(n\) 位同学想排成一行队伍,其中有些同学之间关系非常好,在队伍里需要排在相邻的位置。具体来说,有 \(m\) 对这样的关系(\(m\) 是一个非负整数)。当 \(m\geq 1\) 时,第 \(i\) 对关系(\(1\leq i\leq m\))给出 \(a_i,b_i\),表示排队时编号为 \(a_i\) 的同学需要排在编号为 \(b_i\) 的同学前面,并且两人在队伍中相邻。

现在小杨想知道总共有多少种排队方式。由于答案可能很大,你只需要求出答案对 \(10^9+7\) 取模的结果。

【输入】

第一行,两个整数 \(n,m\),分别表示同学们的数量与关系数量。

接下来 \(m\) 行,每行两个整数 \(a_i,b_i\),表示一对关系。

【输出】

一行,一个整数,表示答案对 \(10^9+7\) 取模的结果。

【输入样例】

4 2
1 3
2 4

【输出样例】

2

【核心思想】

  1. 问题分析\(n\) 位同学排成一行,\(m\) 对关系要求 \(a_i\) 排在 \(b_i\) 前面且相邻。求合法排队方案数对 \(10^9+7\) 取模。本质是链表构建 + 排列组合问题,核心在于将相邻约束转化为链表结构,再计算链的排列数。

  2. 算法选择

    • 链表构建 + 环检测:用 l[]r[] 数组记录每个节点的左右邻居,构建若干条链
    • 阶乘计算排列数:若有 \(cnt\) 条互不干扰的链,则方案数为 \(cnt!\)
  3. 关键步骤

    • 读入数据\(n\)\(m\),以及 \(m\) 对关系 \((a_i, b_i)\)
    • 构建链表并检测矛盾
      • \(l[b] \neq 0\)\(l[b] \neq a\)\(b\) 左边已有其他人),或 \(r[a] \neq 0\)\(r[a] \neq b\)\(a\) 右边已有其他人),输出 \(0\)
      • 否则:\(l[b] = a\)\(r[a] = b\)
    • DFS 遍历链并检测环
      • 对每个链头(\(l[i] = 0\))开始 DFS,统计访问节点数 \(num\)
      • 若访问到已访问节点,说明有环,输出 \(0\)
    • 验证完整性:若 \(num \neq n\),说明有孤立节点无法排入链,输出 \(0\)
    • 计算答案\(ans = cnt! \bmod (10^9+7)\),其中 \(cnt\) 为链的数量
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),构建链表 \(O(m)\),DFS 遍历 \(O(n)\),阶乘计算 \(O(cnt) \leq O(n)\)
    • 空间复杂度:\(O(n)\),链表数组和访问标记
  5. 链表构建与排列组合的核心思想

    • 相邻约束的链表化:每对关系 \(a \to b\) 表示 \(a\)\(b\) 必须相邻且 \(a\) 在左。多个关系可能连接成链(如 \(1 \to 3 \to 5\)),链内顺序固定,链间可任意排列
    • 矛盾检测的完备性:一个节点最多有一个左邻居和一个右邻居,违反此约束即存在矛盾(如 \(a \to b\)\(c \to b\))。同时检测环(如 \(1 \to 2 \to 1\))和孤立节点
    • 链的排列独立性:每条链作为一个整体参与排列,\(cnt\) 条链的全排列数为 \(cnt!\)。链内部顺序已由约束唯一确定,无需额外计算
    • DFS 的环检测:从链头出发沿 r[] 指针遍历,若遇到已访问节点则说明存在环(矛盾关系),立即输出 \(0\)
    • 适用于相邻约束排列、链表构建、环检测类问题

【算法标签】

普及 #排列组合

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long                   // 将int宏定义为long long,防止阶乘计算溢出
const int N = 200005, mod = 1e9+7;    // N为最大节点数常量,mod为取模数1e9+7
int n, m, num;                          // n为同学数量,m为关系数量,num为DFS已访问的节点数
int l[N], r[N];                         // l[u]表示u左边的同学编号,r[u]表示u右边的同学编号(构建链表关系)
bool vis[N];                            // vis[u]标记节点u是否已被访问(用于检测环)

void dfs(int u)
// 深度优先搜索,从链头u开始遍历整条链,统计链长度并检测是否有环
{
    if (vis[u])                         // 如果u已经被访问过,说明存在环(矛盾关系)
    {
        cout << 0 << endl;              // 输出0表示不存在合法排队方案
        exit(0);                        // 直接退出程序
    }
    num++;                              // 已访问节点数加1
    vis[u] = 1;                         // 标记u已访问
    if (r[u]!=0)                        // 如果u有右边的相邻同学
        dfs(r[u]);                      // 递归访问右边的同学,继续遍历链条
}

signed main()                           // 使用signed main是因为#define int long long后,main返回值类型需要显式声明
{
    cin >> n >> m;                      // 读入同学数量n和关系数量m
    for (int i=1; i<=m; i++)           // 循环读入m对相邻关系
    {
        int a, b; cin >> a >> b;        // 读入一对关系:a需要排在b前面且相邻(a在b左边)
        // 检查关系是否矛盾:
        // 1. b已经有左边的人l[b],且不是a(b左边只能有一个人)
        // 2. a已经有右边的人r[a],且不是b(a右边只能有一个人)
        if (l[b]!=0 && l[b]!=a || r[a]!=0 && r[a]!=b)
        {
            cout << 0 << endl;          // 关系矛盾,不存在合法方案
            return 0;                   // 直接结束程序
        }
        l[b] = a;                       // 记录b的左边是a
        r[a] = b;                       // 记录a的右边是b
    }
    int cnt = 0;                        // cnt记录链条的数量(每个链头对应一条链)
    for (int i=1; i<=n; i++)           // 遍历所有同学,寻找链头(左边没有人的同学)
    {
        if (l[i]==0)                    // 如果i的左边没有人,说明i是一条链的链头
        {
            cnt++;                      // 链条数量加1
            dfs(i);                     // 从链头i开始DFS遍历整条链
        }
    }
    if (num!=n)                         // 如果DFS访问的节点数不等于n,说明有节点不在任何链中(孤立点或矛盾)
    {
        cout << 0 << endl;              // 存在无法排入链的节点,不存在合法方案
        return 0;                       // 直接结束程序
    }
    int ans = 1;                        // ans为最终答案,初始化为1
    for (int i=1; i<=cnt; i++)         // 计算cnt条链的全排列数
        ans = ans * i % mod;            // ans = cnt! % mod,即cnt条链可以有cnt!种排列方式
    cout << ans << endl;                // 输出合法排队方案数对1e9+7取模的结果
    return 0;
}

【运行结果】

4 2
1 3
2 4
2
posted @ 2026-08-27 11:06  团爸讲算法  阅读(14)  评论(0)    收藏  举报