造数据相关知识

· · 科技·工程

Python

CYaRon(头文件)

CYaRon:测试数据生成利器

C++

注意事项

Testlib(头文件)

Testlib——最强出题辅助工具库

批量生成数据

推荐使用命令行参数 + bat/sh 的方法。

例如:

gen.cpp:

#include "testlib.h"

using namespace std;

int n, m, k;
vector<int> p;

int main(int argc, char* argv[]) {
  registerGen(argc, argv, 1);

  int i;

  n = atoi(argv[1]);
  m = atoi(argv[2]);
  k = rnd.next(1, n);

  for (i = 1; i <= n; ++i) p.push_back(i);

  shuffle(p.begin(), p.end());
  // 使用 rnd.next() 进行 shuffle

  printf("%d %d %d\n", n, m, k);
  for (i = 0; i < n; ++i) {
    printf("%d%c", p[i], " \n"[i == n - 1]);
    // 把字符串当作数组用,中间空格,末尾换行,是一个造数据时常用的技巧
  }

  return 0;
}

gen_scripts.bat:

gen 10 10 > 1.in
gen 1 1 > 2.in
gen 100 200 > 3.in
gen 2000 1000 > 4.in
gen 100000 100000 > 5.in

这样做的好处是,对于不同的数据只需要写一个 generator,并且可以方便地修改某个测试点的参数。

----OI Wiki 出题

造数据(模板)

data1.cpp:(批量生成数据)

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
namespace rad {
    mt19937_64 R(time(0));
    inline int Rand(int l, int r) {
        uniform_int_distribution<int> distribution(l, r);
        return distribution(R);
    }
} using namespace rad;
int main() {
    string in, out;
    for (int i = 1; i <= 3; i ++) { // 序号
        in.clear();
        int j = i;
        while (j) {
            char m = j % 10 + '0';
            in = m + in;
            j /= 10;
        }
        out = in;
        in += ".in";
        out += ".out";
        string A = "name"; // 文件名
        string B = "name";
        A = A + in, B = B + out;
        in = A, out = B;
        freopen(in.data(), "w", stdout);
        system("data.exe"); // 数据生成器
        fclose(stdout);
        string Str = "std.exe < " + in + " > " + out; // 你的 std
        system(Str.data());
    }
}

data.cpp:(模板)

#include <bits/stdc++.h>
#define int long long
using namespace std;
/*
* __int128 读写支持
* 注意:部分编译器可能不支持__int128类型
*/

/**
 * @brief 从标准输入读取一个__int128类型的整数
 * @details 该函数从标准输入中读取字符序列,解析为__int128类型的整数,支持负数。
 *          它会跳过非数字字符,直到遇到数字或负号,并正确处理多位数字。
 * @return 返回读取到的__int128类型整数
 * @warning 如果输入超出__int128范围,可能导致未定义行为
 */
__int128 read() {
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

/**
 * @brief 将__int128类型的整数输出到标准输出
 * @details 该函数将__int128类型的整数按十进制形式输出到标准输出,支持负数。
 *          它通过递归方式处理多位数字,逐位输出。
 * @param x 要输出的__int128类型整数
 * @return 无返回值
 */
void write(__int128 x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

/*
* 128位整数随机生成器
* 功能:生成指定范围的128位整数
* 原理:组合两个64位随机数生成完整128位空间
*/

/**
 * @brief 128位整数均匀分布随机数生成器类
 * @details 该类用于生成指定范围内的__int128类型随机整数,利用两个64位随机数拼接生成128位随机数。
 *          它基于C++的随机数引擎mt19937_64实现。
 */
class uniform_int128_distribution {
private:
    using u128 = unsigned __int128;
    u128 _min, _max;  // 范围边界
    mt19937_64 _gen;  // 64位随机引擎

public:
    /**
     * @brief 构造函数,初始化随机数生成器的范围
     * @details 验证输入范围的有效性,并初始化随机数引擎。
     * @param a 随机数范围下限
     * @param b 随机数范围上限
     * @throws invalid_argument 如果范围无效(a > b)
     */
    uniform_int128_distribution(__int128 a, __int128 b) : _gen(mt19937_64(random_device{}())) {
        if (a > b) throw invalid_argument("Invalid range");
        _min = static_cast<u128>(a);
        _max = static_cast<u128>(b);
    }

    /**
     * @brief 生成一个指定范围内的随机__int128整数
     * @details 通过组合两个64位随机数生成128位随机数,并将其映射到指定范围。
     * @return 返回一个在[min, max]范围内的__int128类型随机整数
     */
    __int128 operator()() {
        u128 rand_val = (static_cast<u128>(_gen()) << 64) | _gen();
        u128 range = _max - _min + 1;
        if (range == 0) return static_cast<__int128>(rand_val); // 处理全范围
        return static_cast<__int128>(_min + rand_val % range);
    }
};

/*
* 随机数生成命名空间
* 包含各种数据结构的随机生成方法
*/
namespace rad {
    mt19937_64 R(random_device{}()); // 全局随机引擎

    /*
    * 基础随机数生成函数
    * 模板参数:整数类型
    * 参数:l-范围下限,r-范围上限
    */

    /**
     * @brief 生成指定范围内的随机整数
     * @details 该函数生成类型为T的随机整数,范围为[l, r],基于C++的均匀分布随机数生成器。
     * @tparam T 整数类型(如int, long long等)
     * @param l 随机数范围下限
     * @param r 随机数范围上限
     * @return 返回一个在[l, r]范围内的随机整数
     * @throws static_assert 如果T不是整数类型
     */
    template<typename T>
    T Rand(T l, T r) {
        static_assert(is_integral<T>::value, "T must be integral type");
        uniform_int_distribution<T> dis(l, r);
        return dis(R);
    }

    /**
     * @brief 生成指定范围内的随机浮点数(long double特化版本)
     * @details 该函数生成范围为[l, r]的随机浮点数,基于C++的均匀分布随机数生成器。
     * @param l 随机数范围下限
     * @param r 随机数范围上限
     * @return 返回一个在[l, r]范围内的随机浮点数
     */
    template<>
    long double Rand<long double>(long double l, long double r) {
        uniform_real_distribution<long double> dis(l, r);
        return dis(R);
    }

    /*
    * 生成128位整数
    * 参数:l-下限,r-上限
    * 注意:输出需使用write函数
    */

    /**
     * @brief 生成指定范围内的随机__int128整数
     * @details 该函数利用uniform_int128_distribution类生成范围为[l, r]的随机__int128整数。
     * @param l 随机数范围下限
     * @param r 随机数范围上限
     * @return 返回一个在[l, r]范围内的随机__int128整数
     */
    __int128 Rand128(__int128 l, __int128 r) {
        static uniform_int128_distribution gen(l, r);
        return gen();
    }

    /*
    * 容器操作函数
    * Shuffle:随机打乱容器元素
    * RandChoice:从容器中随机选取元素
    */

    /**
     * @brief 随机打乱向量中的元素
     * @details 该函数使用C++的shuffle算法,基于全局随机引擎R,随机打乱向量v中的元素。
     * @tparam T 向量元素类型
     * @param v 待打乱的向量(引用传递,会修改原向量)
     * @return 无返回值
     */
    template<typename T>
    void Shuffle(vector<T>& v) {
        shuffle(v.begin(), v.end(), R);
    }

    /**
     * @brief 从向量中随机选择一个元素
     * @details 该函数从非空向量v中随机选择一个元素并返回其引用。
     * @tparam T 向量元素类型
     * @param v 待选择的向量(const引用,不会修改原向量)
     * @return 返回随机选择的元素的const引用
     * @throws runtime_error 如果向量为空
     */
    template<typename T>
    const T& RandChoice(const vector<T>& v) {
        if (v.empty()) throw runtime_error("Cannot choose from empty vector");
        return v[Rand<size_t>(0, v.size() - 1)];
    }
    /*
    * 树生成器类
    * 支持生成多种树结构
    */
    class Tree {
    public:
        enum Type {
            RANDOM, // 随机树(默认)
            CHAIN,  // 链状结构
            STAR,   // 星型结构
            BINARY  // 近似二叉树结构
        };

        // 边结构体:u起点,v终点,w边权
        struct Edge {
            int u, v, w;
            Edge(int _u, int _v, int _w = 1) : u(_u), v(_v), w(_w) {}
        };

        /**
         * @brief 生成指定类型的树结构
         * @details 该函数根据指定的树类型生成一棵包含n个节点的树,支持随机树、链状树、星型树和近似二叉树。
         *          边的权值在[min_w, max_w]范围内随机生成。
         * @param n 节点数
         * @param type 树类型,默认为随机树(RANDOM)
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @return 返回一个包含所有边的向量,每条边由Edge结构体表示
         * @throws invalid_argument 如果节点数n <= 0
         */
        static vector<Edge> generate(int n, Type type = RANDOM,
            int min_w = 1, int max_w = 1e9) {
                if (n <= 0) throw invalid_argument("Node count must be positive");
                vector<Edge> edges;
                switch (type) {
                    case CHAIN:  // 链状生成
                    for (int i = 2; i <= n; ++i)
                        edges.emplace_back(i - 1, i, Rand(min_w, max_w));
                    break;
                    case STAR:   // 星型生成
                    for (int i = 2; i <= n; ++i)
                        edges.emplace_back(1, i, Rand(min_w, max_w));
                    break;
                    case BINARY: { // 二叉树生成
                        queue<int> q;
                        q.push(1);
                        int cnt = 1;
                        while (cnt < n) {
                            int u = q.front();
                            q.pop();
                            int left = ++cnt;
                            edges.emplace_back(u, left, Rand(min_w, max_w));
                            q.push(left);
                            if (cnt >= n) break;
                            int right = ++cnt;
                            edges.emplace_back(u, right, Rand(min_w, max_w));
                            q.push(right);
                        }
                        break;
                    }
                    default:  // 随机生成(Prufer序列方式)
                    for (int i = 2; i <= n; ++i) {
                        int fa = Rand(1LL, i - 1); // 使用 long long 字面量
                        edges.emplace_back(fa, i, Rand(min_w, max_w));
                    }
                }
                shuffle_edges(edges, n);  // 打乱节点编号和边顺序
                return edges;
            }

        // 定义输出方式的枚举
        enum OutputFormat {
            EDGE_LIST,      // 边列表(默认)
            ADJ_LIST,       // 邻接表
            PARENT_ARRAY    // 父节点数组
        };

        /**
         * @brief 封装的树生成和输出函数
         * @details 该函数生成指定类型的树,并根据指定的输出格式打印树结构。
         *          支持带文字描述和无文字描述的输出,通过verbose参数控制。
         * @param n 节点数
         * @param type 树类型
         * @param format 输出格式
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果节点数n <= 0或输出格式无效
         */
        static void generate_and_print_tree(int n, Type type, OutputFormat format,
            int min_w = 1, int max_w = 1e9, bool verbose = true) {
                // 生成树
                auto edges = generate(n, type, min_w, max_w);

                // 根据选择的格式输出树
                switch (format) {
                    case EDGE_LIST: { // 边列表格式
                        if (verbose) {
                            cout << "边列表格式 (u - v 边权: w):\n";
                            for (const auto& e : edges) {
                                cout << e.u << " - " << e.v << " 边权: " << e.w << "\n";
                            }
                        } else {
                            // 无文字描述,仅输出数据
                            for (const auto& e : edges) {
                                cout << e.u << " " << e.v << " " << e.w << "\n";
                            }
                        }
                        break;
                    }
                    case ADJ_LIST: { // 邻接表格式
                        // 构建邻接表
                        vector<vector<pair<int, int >>> adj(n + 1); // 邻接表,pair<邻居, 边权>
                        for (const auto& e : edges) {
                            adj[e.u].emplace_back(e.v, e.w);
                            adj[e.v].emplace_back(e.u, e.w); // 无向图,双向添加
                        }
                        if (verbose) {
                            cout << "邻接表格式:\n";
                            for (int i = 1; i <= n; ++i) {
                                if (!adj[i].empty()) {
                                    cout << "节点 " << i << " 的邻居: ";
                                    for (const auto& [v, w] : adj[i]) {
                                        cout << "(" << v << ", 边权: " << w << ") ";
                                    }
                                    cout << "\n";
                                }
                            }
                        }
                        else {
                            // 无文字描述,仅输出数据
                            // 每行格式:节点编号 邻居数量 邻居1 边权1 邻居2 边权2 ...
                            for (int i = 1; i <= n; ++i) {
                                if (!adj[i].empty()) {
                                    cout << i << " " << adj[i].size();
                                    for (const auto& [v, w] : adj[i]) {
                                        cout << " " << v << " " << w;
                                    }
                                    cout << "\n";
                                }
                            }
                        }
                        break;
                    }
                    case PARENT_ARRAY: { // 父节点数组格式
                        // 构建父节点数组
                        vector<int> parent(n + 1, -1); // 父节点数组,-1 表示根节点
                        vector<int> weights(n + 1, 0); // 记录到父节点的边权
                        // 使用 BFS 确定父节点关系
                        vector<vector<pair<int, int >>> adj(n + 1); // 邻接表,用于 BFS
                        for (const auto& e : edges) {
                            adj[e.u].emplace_back(e.v, e.w);
                            adj[e.v].emplace_back(e.u, e.w); // 无向图,双向添加
                        }
                        // BFS 确定树的层次结构
                        queue<int> q;
                        vector<bool> visited(n + 1, false);
                        int root = edges[0].u; // 假设第一条边的起点是根(可以改进)
                        q.push(root);
                        visited[root] = true;
                        while (!q.empty()) {
                            int u = q.front();
                            q.pop();
                            for (const auto& [v, w] : adj[u]) {
                                if (!visited[v]) {
                                    visited[v] = true;
                                    parent[v] = u;
                                    weights[v] = w;
                                    q.push(v);
                                }
                            }
                        }
                        if (verbose) {
                            cout << "父节点数组格式 (parent[i] 表示节点 i 的父节点):\n";
                            cout << "根节点: " << root << "\n";
                            cout << "节点 | 父节点 | 到父节点的边权\n";
                            for (int i = 1; i <= n; ++i) {
                                if (i != root) { // 根节点没有父节点
                                    if (parent[i] != -1) { // 确保节点已访问
                                        cout << i << "    | " << parent[i] << "      | " << weights[i] << "\n";
                                    }
                                }
                            }
                        } else {
                            // 无文字描述,仅输出数据
                            // 格式:根节点编号\n节点编号 父节点编号 边权(仅非根节点)
                            cout << root << "\n";
                            for (int i = 1; i <= n; ++i) {
                                if (i != root && parent[i] != -1) {
                                    cout << i << " " << parent[i] << " " << weights[i] << "\n";
                                }
                            }
                        }
                        break;
                    }
                default:
                    throw invalid_argument("Unknown output format");
                }
            }

    private:
        /**
         * @brief 边随机化处理:打乱节点编号和边顺序
         * @details 该函数打乱树的节点编号和边顺序,确保节点编号在 [1, n] 内,增加随机性。
         * @param edges 待处理的边向量(引用传递,会修改原向量)
         * @param n 节点数
         * @return 无返回值
         */
        static void shuffle_edges(vector<Edge>& edges, int n) {
            if (edges.empty()) return;
            // 生成 1 到 n 的随机排列
            vector<int> perm(n);
            iota(perm.begin(), perm.end(), 1);
            shuffle(perm.begin(), perm.end(), R);
            // 映射节点编号
            for (auto& e : edges) {
                if (Rand(0LL, 1LL)) swap(e.u, e.v); // 随机方向
                e.u = perm[e.u - 1]; // 映射到随机节点编号
                e.v = perm[e.v - 1];
            }
            // 随机化边顺序
            rad::Shuffle(edges); // 显式指定 rad 命名空间
        }
    };

    /*
    * 图生成器类
    * 支持生成多种图结构
    */
    class Graph {
    public:
        enum OutputFormat {
            EDGE_LIST,       // 默认边列表(每行u v w)
            TRIPLE_LINES,    // 三行分别输出u、v、w数组
            COLUMNAR,        // 列式存储:所有u一行,所有v一行,所有w一行
            INTERLEAVED_LINE,// 所有边数据挤在一行
            ADJACENCY_LIST  // 邻接表格式
        };

        struct Edge {
            int u, v, w;
        };
        static void print_array(const vector<int>& arr, const string& prefix, bool verbose) {
            if(verbose) cout << prefix;
            for(int x : arr) cout << x << " ";
            cout << "\n";
        }
        /**
         * @brief 生成完全随机图
         * @details 该函数生成包含n个节点和m条边的随机图,边的权值在[min_w, max_w]范围内随机生成
         *          支持有向图和无向图,通过directed参数控制
         * @param n 节点数
         * @param m 边数
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @param directed 是否生成有向图,默认为false
         * @return 返回一个包含所有边的向量
         * @throws invalid_argument 如果参数不合法
         */
        static vector<Edge> random(int n, int m,
            int min_w = 1, int max_w = 1e9,
            bool directed = false) {
                if (n <= 0) throw invalid_argument("Node count must be positive");
                int max_edges = directed ? n*(n-1) : n*(n-1)/2;
                if (m < 0 || m > max_edges)
                    throw invalid_argument("Invalid edge count");

                vector<Edge> edges;
                set<pair<int, int>> edge_set;

                while (edges.size() < m) {
                    int u = Rand(1LL, n);
                    int v = Rand(1LL, n);
                    if (u == v) continue;

                    // 无向图统一方向存储
                    if (!directed && u > v) swap(u, v);

                    if (edge_set.count({u, v})) continue;

                    edges.push_back({u, v, Rand(min_w, max_w)});
                    edge_set.insert({u, v});
                }
                return edges;
            }
        /**
         * @brief 生成并打印随机图(多格式版)
         * @param output_format 输出格式枚举值
         * @param sorted 是否按边权排序后输出
         */
        static void generate_and_print_random_graph(
            int n, int m,
            OutputFormat output_format = EDGE_LIST,
            bool sorted = false,
            int min_w = 1, int max_w = 1e9,
            bool directed = false, 
            bool verbose = true) 
        {
            auto edges = random(n, m, min_w, max_w, directed);

            // 预处理排序
            if(sorted) {
                sort(edges.begin(), edges.end(), 
                    [](const Edge& a, const Edge& b){ return a.w < b.w; });
            }

            // 根据格式输出
            switch(output_format) {
                case EDGE_LIST: 
                if(verbose) cout << "边列表格式:\n";
                for(const auto& e : edges) 
                    cout << e.u << " " << e.v << " " << e.w << "\n";
                break;

                case TRIPLE_LINES: {
                    if(verbose) cout << "三行格式:\n";
                    // 生成u数组
                    for(const auto& e : edges) cout << e.u << " ";
                    cout << "\n";
                    // 生成v数组 
                    for(const auto& e : edges) cout << e.v << " ";
                    cout << "\n";
                    // 生成w数组
                    for(const auto& e : edges) cout << e.w << " ";
                    cout << "\n";
                    break;
                }

                case COLUMNAR: {
                    if(verbose) cout << "列式存储:\n";
                    vector<int> us, vs, ws;
                    for(const auto& e : edges) {
                        us.push_back(e.u);
                        vs.push_back(e.v);
                        ws.push_back(e.w);
                    }
                    print_array(us, "U: ", verbose);
                    print_array(vs, "V: ", verbose);
                    print_array(ws, "W: ", verbose);
                    break;
                }

                case INTERLEAVED_LINE: 
                if(verbose) cout << "单行交错格式:\n";
                for(const auto& e : edges) 
                    cout << e.u << " " << e.v << " " << e.w << " ";
                cout << "\n";
                break;

                case ADJACENCY_LIST: {
                    if(verbose) cout << "邻接表格式:\n";
                    map<int, vector<pair<int, int>>> adj;
                    for(const auto& e : edges) {
                        adj[e.u].emplace_back(e.v, e.w);
                        if(!directed)
                            adj[e.v].emplace_back(e.u, e.w);
                    }
                    for(const auto& [u, neighbors] : adj) {
                        cout << u << ": ";
                        for(const auto& [v, w] : neighbors)
                            cout << "(" << v << "," << w << ") ";
                        cout << "\n";
                    }
                    break;
                }
            }
        }
        /**
         * @brief 生成连通图
         * @details 该函数生成一个包含n个节点和m条边的连通图,边的权值在[min_w, max_w]范围内随机生成。
         *          支持有向图和无向图,通过directed参数控制。
         * @param n 节点数
         * @param m 边数(必须 >= n-1 以保证连通性)
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @param directed 是否生成有向图,默认为false
         * @return 返回一个包含所有边的向量,每条边由Edge结构体表示
         * @throws invalid_argument 如果节点数n <= 0 或边数m < n-1
         */
        static vector<Edge> connected(int n, int m,
            int min_w = 1, int max_w = 1e9,
            bool directed = false) {
                if (n <= 0) throw invalid_argument("Node count must be positive");
                if (m < n - 1) throw invalid_argument("Edge count must be at least n-1 for connectivity");
                auto tree = Tree::generate(n); // 先生成树保证连通
                vector<Edge> edges;
                set<pair<int, int >> edge_set; // 用于去重

                // 添加树边
                for (auto& e : tree) {
                    edges.push_back({e.u, e.v, Rand(min_w, max_w)});
                    if (directed) {
                        edge_set.insert({e.u, e.v});
                    }
                    else {
                        edge_set.insert({min(e.u, e.v), max(e.u, e.v)});
                    }
                }

                // 补充剩余边
                while ((int)edges.size() < m) {
                    int u = Rand(1LL, n);
                    int v = Rand(1LL, n);
                    if (u == v) continue; // 排除自环
                    if (!directed && u > v) swap(u, v); // 无向图统一顺序
                    if (edge_set.count({u, v})) continue; // 排除重边

                    edges.push_back({u, v, Rand(min_w, max_w)});
                    edge_set.insert({u, v});
                }
                return edges;
            }

        /**
         * @brief 生成连通图并打印
         * @details 该函数生成连通图,并根据verbose参数选择带文字描述或无文字描述的输出。
         * @param n 节点数
         * @param m 边数
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @param directed 是否生成有向图,默认为false
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果节点数n <= 0 或边数m < n-1
         */
        static void generate_and_print_graph(int n, int m,
            int min_w = 1, int max_w = 1e9,
            bool directed = false, bool verbose = true) {
                auto edges = connected(n, m, min_w, max_w, directed);
                if (verbose) {
                    cout << "连通图边列表格式 (" << (directed ? "u -> v" : "u - v") << " 边权: w):\n";
                    for (const auto& e : edges) {
                        cout << e.u << (directed ? " -> " : " - ") << e.v << " 边权: " << e.w << "\n";
                    }
                } else {
                    // 无文字描述,仅输出数据
                    for (const auto& e : edges) {
                        cout << e.u << " " << e.v << " " << e.w << "\n";
                    }
                }
            }

        /**
         * @brief 生成有向无环图(DAG)
         * @details 该函数生成一个包含n个节点和m条边的有向无环图,边的权值在[min_w, max_w]范围内随机生成。
         *          通过拓扑排序生成边,确保无环。
         * @param n 节点数
         * @param m 边数
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @return 返回一个包含所有边的向量,每条边由Edge结构体表示
         * @throws invalid_argument 如果节点数n <= 0 或边数m < 0
         */
        static vector<Edge> DAG(int n, int m, int min_w = 1, int max_w = 1e9) {
            if (n <= 0) throw invalid_argument("Node count must be positive");
            if (m < 0) throw invalid_argument("Edge count must be non-negative");
            vector<int> ord(n);
            iota(ord.begin(), ord.end(), 1);
            Shuffle(ord); // 随机拓扑序

            set<pair<int, int >> edges;
            while (edges.size() < m) {
                int u = Rand(0LL, n - 2); // 保证边的方向性
                int v = Rand(u + 1, n - 1);
                edges.insert({ord[u], ord[v]});
            }

            vector<Edge> res;
            for (auto& [u, v] : edges)
                res.push_back({u, v, Rand(min_w, max_w)});
            return res;
        }

        /**
         * @brief 生成有向无环图(DAG)并打印
         * @details 该函数生成有向无环图,并根据verbose参数选择带文字描述或无文字描述的输出。
         * @param n 节点数
         * @param m 边数
         * @param min_w 最小边权,默认为1
         * @param max_w 最大边权,默认为1e9
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果节点数n <= 0 或边数m < 0
         */
        static void generate_and_print_dag(int n, int m, int min_w = 1, int max_w = 1e9, bool verbose = true) {
            auto edges = DAG(n, m, min_w, max_w);
            if (verbose) {
                cout << "有向无环图边列表格式 (u -> v 边权: w):\n";
                for (const auto& e : edges) {
                    cout << e.u << " -> " << e.v << " 边权: " << e.w << "\n";
                }
            } else {
                // 无文字描述,仅输出数据
                for (const auto& e : edges) {
                    cout << e.u << " " << e.v << " " << e.w << "\n";
                }
            }
        }
    };

    /*
    * 数组生成器类
    * 生成特殊结构的数组
    */
    class Array {
    public:
        /**
         * @brief 生成随机排列
         * @details 该函数生成一个包含1到n的随机排列。
         * @param n 元素个数
         * @return 返回一个包含随机排列的向量
         * @throws invalid_argument 如果元素个数n <= 0
         */
        static vector<int> permutation(int n , int l , int r) {
            if (n <= 0) throw invalid_argument("Size must be positive");
            vector<int> res(n);
            for(int i = 0 ; i < n ; i++) res[i] = Rand(l , r) ;  // 生成l~r
            Shuffle(res);
            return res;
        }
        /**
         * @brief 生成随机排列并打印
         * @details 该函数生成一个包含1到n的随机排列,并根据verbose参数选择带文字描述或无文字描述的输出。
         * @param n 元素个数
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果元素个数n <= 0
         */
        static void generate_and_print_permutation(int n , int l , int r , bool verbose = true) {
            auto perm = permutation(n , l , r);
            if (verbose) {
                cout << "随机排列: ";
                for (int x : perm) cout << x << " ";
                cout << "\n";
            } else {
                // 无文字描述,仅输出数据
                for (int x : perm) cout << x << " ";
                cout << "\n";
            }
        }

        /**
         * @brief 生成唯一元素数组(无重复)
         * @details 该函数生成一个包含n个唯一元素的数组,元素值在[min_v, max_v]范围内随机生成。
         * @tparam T 元素类型
         * @param n 元素个数
         * @param min_v 最小值
         * @param max_v 最大值
         * @return 返回一个包含唯一元素的向量
         * @throws invalid_argument 如果元素个数n <= 0 或范围不足以生成n个唯一元素
         */
        template<typename T>
        static vector<T> unique(int n, T min_v, T max_v) {
            if (n <= 0) throw invalid_argument("Size must be positive");
            if (max_v - min_v + 1 < n) throw invalid_argument("Range too small for unique elements");
            set<T> s;
            while (s.size() < n)
                s.insert(Rand(min_v, max_v));
            vector<T> res(s.begin(), s.end());
            Shuffle(res);
            return res;
        }

        /**
         * @brief 生成唯一元素数组并打印
         * @details 该函数生成一个包含n个唯一元素的数组,并根据verbose参数选择带文字描述或无文字描述的输出。
         * @tparam T 元素类型
         * @param n 元素个数
         * @param min_v 最小值
         * @param max_v 最大值
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果元素个数n <= 0 或范围不足以生成n个唯一元素
         */
        template<typename T>
        static void generate_and_print_unique(int n, T min_v, T max_v, bool verbose = true) {
            auto arr = unique(n, min_v, max_v);
            if (verbose) {
                cout << "唯一元素数组: ";
                for (const auto& x : arr) cout << x << " ";
                cout << "\n";
            } else {
                // 无文字描述,仅输出数据
                for (const auto& x : arr) cout << x << " ";
                cout << "\n";
            }
        }

        /**
         * @brief 生成山峰数组(先递增后递减)
         * @details 该函数生成一个包含n个元素的山峰数组,数组先严格递增后严格递减,元素值在[1, max_val]范围内随机生成。
         * @param n 元素个数
         * @param max_val 最大值限制,默认为1e9
         * @return 返回一个包含山峰数组的向量
         * @throws invalid_argument 如果元素个数n <= 0 或n < 2
         */
        static vector<int> mountain(int n, int max_val = 1e9) {
            if (n <= 0) throw invalid_argument("Size must be positive");
            if (n < 2) throw invalid_argument("Size must be at least 2 for mountain array");
            vector<int> res(n);
            int peak = Rand(1LL, n - 2); // 随机山峰位置
            // 生成递增部分
            for (int i = 0; i <= peak; ++i) res[i] = Rand(1LL, max_val);
            // 生成递减部分
            for (int i = peak + 1; i < n; ++i) res[i] = Rand(1LL, res[i - 1]);
            // 排序形成山峰
            sort(res.begin(), res.begin() + peak + 1);
            sort(res.begin() + peak + 1, res.end(), greater<>());
            return res;
        }

        /**
         * @brief 生成山峰数组并打印
         * @details 该函数生成一个山峰数组,并根据verbose参数选择带文字描述或无文字描述的输出。
         * @param n 元素个数
         * @param max_val 最大值限制,默认为1e9
         * @param verbose 是否输出带文字描述的版本,默认为true
         * @throws invalid_argument 如果元素个数n <= 0 或n < 2
         */
        static void generate_and_print_mountain(int n, int max_val = 1e9, bool verbose = true) {
            auto arr = mountain(n, max_val);
            if (verbose) {
                cout << "山峰数组: ";
                for (int x : arr) cout << x << " ";
                cout << "\n";
            } else {
                // 无文字描述,仅输出数据
                for (int x : arr) cout << x << " ";
                cout << "\n";
            }
        }
    };

    /*
    * 实用工具函数
    */

    /**
     * @brief 生成高精度浮点数(保留x位小数)
     * @details 该函数生成一个范围为[l, r]的高精度浮点数,保留x位小数。
     * @param l 范围下限
     * @param r 范围上限
     * @param x 小数位数
     * @return 返回一个保留x位小数的随机浮点数
     */
    long double RandX(long double l, long double r, int x) {
        long double scale = pow(10, x);
        return round(Rand<long double>(l * scale, r * scale)) / scale;
    }

    /**
     * @brief 生成高精度浮点数并打印
     * @details 该函数生成一个高精度浮点数,并根据verbose参数选择带文字描述或无文字描述的输出。
     * @param l 范围下限
     * @param r 范围上限
     * @param x 小数位数
     * @param verbose 是否输出带文字描述的版本,默认为true
     */
    void generate_and_print_randx(long double l, long double r, int x, bool verbose = true) {
        long double val = RandX(l, r, x);
        if (verbose) {
            cout << "随机高精度浮点数 (保留 " << x << " 位小数): " << fixed << setprecision(x) << val << "\n";
        } else {
            // 无文字描述,仅输出数据
            cout << fixed << setprecision(x) << val << "\n";
        }
    }

    /**
     * @brief 生成随机字符串
     * @details 该函数生成一个长度为len的随机字符串,字符从charset中随机选择。
     * @param len 字符串长度
     * @param charset 字符集,默认为小写字母
     * @return 返回一个随机字符串
     * @throws invalid_argument 如果长度len < 0
     */
    string RandString(int len, const string& charset = "abcdefghijklmnopqrstuvwxyz") {
        if (len < 0) throw invalid_argument("Length must be non-negative");
        string s;
        for (int i = 0; i < len; ++i)
            s += charset[Rand<size_t>(0, charset.size() - 1)];
        return s;
    }

    /**
     * @brief 生成随机字符串并打印
     * @details 该函数生成一个随机字符串,并根据verbose参数选择带文字描述或无文字描述的输出。
     * @param len 字符串长度
     * @param charset 字符集,默认为小写字母
     * @param verbose 是否输出带文字描述的版本,默认为true
     * @throws invalid_argument 如果长度len < 0
     */
    void generate_and_print_randstring(int len, const string& charset = "abcdefghijklmnopqrstuvwxyz", bool verbose = true) {
        string s = RandString(len, charset);
        if (verbose) {
            cout << "随机字符串: " << s << "\n";
        } else {
            // 无文字描述,仅输出数据
            cout << s << "\n";
        }
    }

    /**
     * @brief 生成随机颜色(十六进制格式)
     * @details 该函数生成一个随机的十六进制颜色代码,格式为 #RRGGBB。
     * @return 返回一个随机颜色代码字符串
     */
    string RandColorHex() {
        char buf[8];
        snprintf(buf, sizeof(buf), "#%02X%02X%02X",
            static_cast<int>(Rand(0LL, 255LL)),
            static_cast<int>(Rand(0LL, 255LL)),
            static_cast<int>(Rand(0LL, 255LL)));
        return string(buf);
    }

    /**
     * @brief 生成随机颜色并打印
     * @details 该函数生成一个随机颜色代码,并根据verbose参数选择带文字描述或无文字描述的输出。
     * @param verbose 是否输出带文字描述的版本,默认为true
     */
    void generate_and_print_randcolor(bool verbose = true) {
        string color = RandColorHex();
        if (verbose) {
            cout << "随机颜色 (十六进制): " << color << "\n";
        } else {
            // 无文字描述,仅输出数据
            cout << color << "\n";
        }
    }
    /*
    * 示例程序:展示如何使用上述功能生成数据
    */

    /**
     * @brief 展示所有数据生成功能的完整示例
     * @param verbose 是否显示详细描述
     * @param extreme 是否包含边界测试用例
     */
    void Example(bool verbose = true, bool extreme = false) {

        cout << (verbose ? "===== 数据生成器示例演示 =====\n" : "");

        // ===== 1. 树生成示例 =====
        cout << (verbose ? "\n1. 树结构生成示例:\n" : "");
        {
            // 常规测试用例
            Tree::generate_and_print_tree(5, Tree::RANDOM, Tree::EDGE_LIST, 1, 10, verbose);
            Tree::generate_and_print_tree(6, Tree::CHAIN, Tree::ADJ_LIST, 1, 100, verbose);
            Tree::generate_and_print_tree(4, Tree::STAR, Tree::PARENT_ARRAY, 1, 50, verbose);

            // 边界测试用例
            if(extreme) {
                cout << (verbose ? "\n[边界测试] 大树生成:\n" : "");
                Tree::generate_and_print_tree(100000, Tree::BINARY, Tree::EDGE_LIST, 1, 1e9, false);
            }
        }

        // ===== 2. 图生成示例 ===== 
        cout << (verbose ? "\n2. 图结构生成示例:\n" : "");
        {
            // 常规测试用例
            rad::Graph::generate_and_print_random_graph(5, 7, rad::Graph::EDGE_LIST, false, 1, 20, false, verbose);
            rad::Graph::generate_and_print_random_graph(4, 6, rad::Graph::TRIPLE_LINES, true, 1, 50, true, verbose);

            // 边界测试用例
            if(extreme) {
                cout << (verbose ? "\n[边界测试] 稠密图生成:\n" : "");
                rad::Graph::generate_and_print_random_graph(1000, 499500, rad::Graph::COLUMNAR, false, 1, 1e9, false, false);
            }
        }

        // ===== 3. 数组生成示例 =====
        cout << (verbose ? "\n3. 特殊数组生成示例:\n" : "");
        {
            // 常规测试用例
            Array::generate_and_print_permutation(5 , 1 , 5 ,  verbose);
            Array::generate_and_print_unique(5, 1, 10, verbose);
            Array::generate_and_print_mountain(6, 100, verbose);

            // 边界测试用例
            if(extreme) {
                cout << (verbose ? "\n[边界测试] 大规模山峰数组:\n" : "");
                Array::generate_and_print_mountain(1000000, 1e9, false);
            }
        }

        // ===== 4. 高级功能示例 =====
        cout << (verbose ? "\n4. 高级功能示例:\n" : "");
        {
            // 128位整数
            cout << (verbose ? "128位随机整数: " : "");
            write(Rand128(1e18, 1e20));
            cout << "\n";

            // 高精度浮点数
            generate_and_print_randx(0.0, 1.0, 5, verbose);

            // 随机字符串
            generate_and_print_randstring(10, "ACGT", verbose); // DNA序列

            // 特殊测试用例
            if(extreme) {
                cout << (verbose ? "\n[压力测试] 超长随机字符串:\n" : "");
                generate_and_print_randstring(1000000, "01", false); // 1MB二进制数据
            }
        }

        // ===== 5. 连通性测试示例 =====
        cout << (verbose ? "\n5. 连通性测试示例:\n" : "");
        {
            auto edges = Graph::connected(10, 15, 1, 100);
            if(verbose) {
                cout << "连通图验证(边数=" << edges.size() << "):\n";
                for(auto& e : edges) 
                    cout << e.u << "-" << e.v << "(" << e.w << ") ";
                cout << "\n";
            } else {
                for(auto& e : edges)
                    cout << e.u << " " << e.v << " " << e.w << "\n";
            }
        }

        cout << (verbose ? "\n===== 示例演示结束 =====\n" : "");
    }
    void ZExample() {
        // 运行所有示例,带文字描述
        cout << "运行所有示例(带文字描述):\n";
        Example(true , 0);

        // 运行所有示例,无文字描述
        cout << "\n运行所有示例(无文字描述):\n";
        Example(false , 0);
    }
}
/**
 * @brief 主函数:运行示例并生成数据
 */
signed main(){
//  rad :: ZExample() ;

    return 0;
}

随机数生成命名空间 rad

函数名 功能描述 参数说明 返回值 异常/限制
Rand<T>(l, r) 生成指定范围的随机整数 T l: 下限<br>T r: 上限 类型 T 的随机整数 static_assert 检查整数类型
Rand<long double>(l, r) 生成指定范围的随机浮点数 long double l: 下限<br>long double r: 上限 long double 随机浮点数
Rand128(l, r) 生成128位随机整数 __int128 l: 下限<br>__int128 r: 上限 __int128 随机整数 需用 write() 输出
Shuffle(v) 随机打乱容器元素 vector<T>& v: 待打乱的容器 无(原地修改)
RandChoice(v) 从容器中随机选择元素 const vector<T>& v: 非空容器 const T& 随机元素 runtime_error 如果容器为空
RandX(l, r, x) 生成保留x位小数的随机浮点数 long double l: 下限<br>long double r: 上限<br>int x: 小数位数 long double 随机浮点数
RandString(len, charset) 生成随机字符串 int len: 长度<br>const string& charset: 字符集(默认小写字母) string 随机字符串 invalid_argument 如果长度<0
RandColorHex() 生成随机十六进制颜色 string 格式 #RRGGBB

树生成器类 Tree

函数名 功能描述 参数说明 返回值 异常/限制
generate(n, type, min_w, max_w) 生成指定类型的树 n: 节点数<br>type: 树类型<br>min_w/max_w: 边权范围 vector<Edge> 边列表 invalid_argument 如果节点数≤0
generate_and_print_tree(...) 生成并输出树 generate 增加:<br>format: 输出格式<br>verbose: 是否显示描述 无(输出到stdout) generate

树类型枚举 Type:

输出格式枚举 OutputFormat:

图生成器类 Graph

函数名 功能描述 参数说明 返回值 异常/限制
random(n, m, min_w, max_w, directed) 生成完全随机图 n: 节点数<br>m: 边数<br>min_w/max_w: 边权范围<br>directed: 是否有向 vector<Edge> 边列表 invalid_argument 如果边数超过理论最大值
connected(...) 生成连通图 random vector<Edge> 边列表 invalid_argument 如果边数<节点数-1
DAG(...) 生成有向无环图 random vector<Edge> 边列表 invalid_argument 如果边数<0
generate_and_print_*(...) 生成并输出图(多种格式) 增加:<br>output_format: 输出格式<br>sorted: 是否按边权排序 无(输出到stdout) 同对应生成函数

输出格式枚举 OutputFormat:

数组生成器类 Array

函数名 功能描述 参数说明 返回值 异常/限制
permutation(n, l, r) 生成l~r的随机排列 n: 元素个数<br>l/r: 值域范围 vector<int> 随机排列 invalid_argument 如果n≤0
unique(n, min_v, max_v) 生成唯一元素数组 n: 元素个数<br>min_v/max_v: 值域范围 vector<T> 唯一元素数组 invalid_argument 如果值域不足以生成n个唯一元素
mountain(n, max_val) 生成山峰数组(先增后减) n: 元素个数<br>max_val: 最大值限制 vector<int> 山峰数组 invalid_argument 如果n<2

辅助函数

函数名 功能描述 参数说明 返回值 备注
read() 读取128位整数 __int128 支持负数输入
write(x) 输出128位整数 __int128 x: 要输出的数 支持负数输出
generate_and_print_*(...) 各类型的生成并输出快捷函数 对应生成函数的参数+verbose控制输出格式 简化输出流程

使用场景示例

// 1. 生成随机树(10节点,边权1~100)
auto tree = rad::Tree::generate(10, rad::Tree::RANDOM, 1, 100);

// 2. 生成连通图(100节点,200边,无向)
auto graph = rad::Graph::connected(100, 200, 1, 100);

// 3. 输出128位大整数
__int128 big_num = rad::Rand128(1e30, 1e40);
rad::write(big_num);

// 4. 快速测试(带描述)
rad::Example(true, true); // 启用边界测试

造数据模板