1 条题解

  • 0
    @ 2022-2-23 7:32:37

    在我的个人博客中阅读

    题目链接

    题目大意

    给出一个 nn 个点的 DAG,满足 1, 2, 3, , n1,~2,~3,~\dots,~n 为它的一个拓扑序。问有多少对满足条件的二元组 (x, y)(x,~y) 满足 x<yx < y 且在原图上加入一条 yxy \to x 的有向边后原图存在哈密顿路径。

    n, m1.5×105n,~m \le 1.5 \times 10^5

    分析

    若原图本身存在哈密顿路径,则答案即为 (n2)n \choose 2

    否则我们可以从原图上找出两条链,满足每个点均在其中一条链上,将这两条链首尾相连即可得到哈密顿路径。令最终我们加入的边为 yxy \to x,则哈密顿路径应形如:

    1. 123(x1)1 \to 2 \to 3 \to \cdots \to (x - 1)
    2. (x1)y(x - 1) \to \cdots \to y
    3. yxy \to x
    4. x(y+1)x \to \cdots \to (y + 1)
    5. (y+1)(y+2)n(y + 1) \to (y + 2) \to \cdots \to n

    其中 2、4 两部分包括 (x1)(y+1)(x-1) \sim (y + 1) 之间的所有结点,且 (x1)(x - 1)yy 在同一条链上,xx(y+1)(y + 1) 在另一条链上。若采用状态 (x, y)(x,~y) 表示 xx11 在同一条链上,yynn 在另一条链上,当 (x1, x)(x-1,~x)(y, y+1)(y,~y+1) 状态在同一方案中同时存在时连接 yxy \to x 为一种合法方案。

    将所有结点 aa 与结点 (a+1)(a + 1) 所在链不同的情况拿出,令 Sx=(x, x+1), Tx=(x+1, x)S_x = (x,~x + 1),~T_x = (x + 1,~x)。将这 O(n)O(n) 个状态看为结点,在同时出现于同一方案中的相邻状态对应的结点间连边。不难发现相邻两状态的关系一定形如这样:

    即在 (x+1)(x + 1) 可向右以步长 11 走到 yy 且存在边 x(y+1)x \to (y + 1)Sx=TyS_x = T_y。通过此方法可以在这 O(n)O(n) 个状态间连上 O(m)O(m) 条边。最终答案即为:满足下面条件的数对 (x, y)(x,~y) 数量:

    1. 可从 11 号点以步长 11 走到 xx
    2. 可从 (y+1)(y + 1) 以步长 11 走到 nn
    3. Sx=SyS_x = S_y

    官方题解给出了一种巧妙地方法计算这样的数对的数量:由于原图中不存在哈密顿路径,所以必然存在至少一个点 pp 满足不存在边 p(p+1)p \to (p + 1)。不难发现对于每一对相连的 Sx, SyS_x,~S_y,它们两点之间的路径上必然存在 SpS_pTpT_p。因此建完图后从 SpS_pTpT_p 开始分别向左和向右遍历连通块,将左侧连通块内满足条件 1 的结点数量与右侧连通块内满足条件 2 的结点相乘即可。由于可能存在 SxS_xSyS_y 同时在 SpS_pTpT_p 两侧的连通块中的情况,因此最终需容斥减去算重的部分。

    时间复杂度 O(nlogn)O(n \log n)

    代码

    View on GitHub

    Code
    /**
     * @file 1616G.cpp
     * @author Macesuted (i@macesuted.moe)
     * @date 2022-02-22
     *
     * @copyright Copyright (c) 2022
     *
     */
    
    #include <bits/stdc++.h>
    using namespace std;
    
    #define MP make_pair
    #define MT make_tuple
    
    namespace io {
    #define SIZE (1 << 20)
    char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55];
    int f, qr;
    inline void flush(void) { return fwrite(obuf, 1, oS - obuf, stdout), oS = obuf, void(); }
    inline char getch(void) {
        return (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++);
    }
    void putch(char x) {
        *oS++ = x;
        if (oS == oT) flush();
        return;
    }
    string getstr(void) {
        string s = "";
        char c = getch();
        while (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == EOF) c = getch();
        while (!(c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == EOF)) s.push_back(c), c = getch();
        return s;
    }
    void putstr(string str, int begin = 0, int end = -1) {
        if (end == -1) end = str.size();
        for (int i = begin; i < end; i++) putch(str[i]);
        return;
    }
    template <typename T>
    T read() {
        T x = 0;
        for (f = 1, c = getch(); c < '0' || c > '9'; c = getch())
            if (c == '-') f = -1;
        for (x = 0; c <= '9' && c >= '0'; c = getch()) x = x * 10 + (c & 15);
        return x * f;
    }
    template <typename T>
    void write(const T& t) {
        T x = t;
        if (!x) putch('0');
        if (x < 0) putch('-'), x = -x;
        while (x) qu[++qr] = x % 10 + '0', x /= 10;
        while (qr) putch(qu[qr--]);
        return;
    }
    struct Flusher_ {
        ~Flusher_() { flush(); }
    } io_flusher_;
    }  // namespace io
    using io::getch;
    using io::getstr;
    using io::putch;
    using io::putstr;
    using io::read;
    using io::write;
    
    bool mem1;
    
    #define maxn 150005
    
    bool cons[maxn], vis[2][maxn * 2];
    
    vector<vector<int>> graph, g, gr;
    
    void dfs(int p, vector<vector<int>>& graph, int id) {
        vis[id][p] = true;
        for (auto i : graph[p])
            if (!vis[id][i]) dfs(i, graph, id);
        return;
    }
    
    void solve(void) {
        int n = read<int>() + 2, m = read<int>();
        graph.clear(), g.clear(), gr.clear(), graph.resize(n + 1), g.resize(2 * n + 1), gr.resize(2 * n + 1);
        for (int i = 1; i <= n; i++) cons[i] = false, vis[0][i] = vis[1][i] = vis[0][n + i] = vis[1][n + i] = false;
        for (int i = 3; i < n; i++) graph[1].push_back(i), graph[i - 1].push_back(n);
        cons[1] = cons[n - 1] = true;
        for (int i = 1; i <= m; i++) {
            int x = read<int>() + 1, y = read<int>() + 1;
            if (x + 1 == y)
                cons[x] = true;
            else
                graph[x].push_back(y);
        }
        int p = 0, l = 0, r = 0;
        for (int i = 1; i < n; i++)
            if (!cons[i]) {
                if (!p) p = l = i;
                r = i;
            }
        if (p == 0) return write(1LL * (n - 2) * (n - 3) / 2), putch('\n');
        for (int i = n, r = n; i > 1; i--) {
            if (!cons[i]) r = i;
            for (auto j : graph[i - 1])
                if (j <= r + 1)
                    g[i - 1].push_back(n + j), g[n + i].push_back(j - 1), gr[n + j].push_back(i - 1), gr[j - 1].push_back(n + i);
        }
        dfs(p, g, 0), dfs(p, gr, 0), dfs(n + p + 1, g, 1), dfs(n + p + 1, gr, 1);
        long long ans = 0, cnt1 = 0, cnt2 = 0;
        for (int i = 1; i <= l; i++) cnt1 += vis[0][i];
        for (int i = r; i < n; i++) cnt2 += vis[0][i];
        ans += cnt1 * cnt2, cnt1 = cnt2 = 0;
        for (int i = 1; i <= l; i++) cnt1 += vis[1][i];
        for (int i = r; i < n; i++) cnt2 += vis[1][i];
        ans += cnt1 * cnt2, cnt1 = cnt2 = 0;
        for (int i = 1; i <= l; i++) cnt1 += (vis[0][i] & vis[1][i]);
        for (int i = r; i < n; i++) cnt2 += (vis[0][i] & vis[1][i]);
        ans -= cnt1 * cnt2;
        return write(ans - (l == r)), putch('\n');
    }
    
    bool mem2;
    
    int main() {
    #ifdef MACESUTED
        cerr << "Memory: " << abs(&mem1 - &mem2) / 1024. / 1024. << "MB" << endl;
    #endif
    
        int _ = read<int>();
        while (_--) solve();
    
    #ifdef MACESUTED
        cerr << "Time: " << clock() * 1000. / CLOCKS_PER_SEC << "ms" << endl;
    #endif
        return 0;
    }
    
    • 1

    信息

    ID
    7577
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者