P11380 [GESP202412 八级] 排队

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

相邻且有方向的关系只能组成若干条链,合法时答案是链数的阶乘。

容易误入的路

只检查连通性不够,入度/出度冲突和有向环都会使答案为 0。

二、核心观察

建模与压缩

nxt[a]=b 表示 a 后面紧跟 b;pre[b]=a 表示 b 前面紧跟 a。

为什么不丢答案:一条链内部顺序已固定,只剩把所有链整体排列。

pre/nxt 并查集 链数

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

为什么环不合法?

问题 2

孤立点算不算链?

五、手推结果

检查失败条件
出度同一人后面跟两人
入度两人都在某人前
并查集已连通

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MOD = 1e9 + 7;

struct DSU {
    vector<int> parent;
    DSU(int n) {
        parent.resize(n + 1);
        iota(parent.begin(), parent.end(), 0);
    }
    int find(int i) {
        if (parent[i] == i) return i;
        return parent[i] = find(parent[i]);
    }
    bool unite(int i, int j) {
        int root_i = find(i);
        int root_j = find(j);
        if (root_i != root_j) {
            parent[root_i] = root_j;
            return true;
        }
        return false;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m;
    
    vector<int> nxt(n + 1, 0);
    vector<int> pre(n + 1, 0);
    DSU dsu(n);
    int edge_count = 0;
    bool possible = true;
    
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        if (!possible) continue;
        
        if (a == b) { possible = false; continue; }
        
        // 如果 a 已经排在别人前面,或者 b 前面已经是别人了
        if ((nxt[a] != 0 && nxt[a] != b) || (pre[b] != 0 && pre[b] != a)) {
            possible = false;
            continue;
        }
        
        // 如果是已经存在的规则,跳过
        if (nxt[a] == b) continue;
        
        // 环检测
        if (dsu.find(a) == dsu.find(b)) {
            possible = false;
            continue;
        }
        
        dsu.unite(a, b);
        nxt[a] = b;
        pre[b] = a;
        edge_count++;
    }
    
    if (!possible) {
        cout << 0 << endl;
        return 0;
    }
    
    // 剩下的连通块数量(每条边减少一个连通块)
    int components = n - edge_count;
    
    ll ans = 1;
    for (int i = 1; i <= components; i++) {
        ans = (ans * i) % MOD;
    }
    
    cout << ans << endl;
    
    return 0;
}