loading...qwq

新年的新航线

statement

舒克和贝塔成立了一家航空公司。因为新年的来临,舒克贝塔打算重新规划航空路线。

舒克和贝塔的航空公司控制了 \(n\) 个机场,这 \(n\) 个机场的位置在地图上可看做一个正 \(n\) 边形的 \(n\) 个顶点,按顺时针依次编号为 \(1, \dots, n\)

舒克和贝塔的航空公司掌握着这 \(n\) 个机场间的 \(2n-3\) 条双向航线,且他们恰好构成了这个正 \(n\) 边形的所有边和一个三角剖分。也即,如果把机场作为点,航线作为线画在地图上,那么我们可以看到这些航线只可能在端点处相交,且最外围的航线构成了那个正 \(n\) 边形的边,而正 \(n\) 边形内部被其他航线完全分成了一个个三角形。

新年来了,航空公司人员纷纷放假,所以舒克希望新规范的航线图,航线尽量少,但要保证乘客能从原来的航线图的任何两个地方往返(也就是说你希望找一棵原图的生成树)。

同时,为了提高运力,贝塔希望新规范的航线图,不存在一个机场恰好能和两个机场直接通过航线相连(也就是说任何一个节点的度数不为 \(2\))。

此外,受到航空管制的影响,你不能额外申请航线,只能取消部分航线。

现在它们找到了跳蚤,希望能帮助它们找出一个合法的方案。

sol

为什么三角剖分图里面是 \(n - 3\) 条边?

假设是 \(x\) 条边,思考一条一条加入的过程可知图里面有 \(x + 1\) 个三角形。

依据边数「算两次」列出方程:\(3(x + 1) = 2x + n\)

所以 \(x = n - 3\)

注意到:

假设是 \(x\) 条边,思考一条一条加入的过程可知图里面有 \(x + 1\) 个三角形。

所以其实三角剖分作为平面图的对偶图是树,并且是二叉树。

构建这棵树需要不断删掉二度点,实际上本题可以不显式建树。

构造

Part1

下图结构为原图的部分,\((5, 6)\) 是树上的边,实际不存在,其中 \(6\) 是树的叶子。

image

考虑删掉点 \(3\)\(2\),如下构造,选择 \((1, 3)\)\((1, 2)\) 作为生成树的边。

image

删去 \(3\)\(2\) 后剩下的图中 \(1\)\(4\) 度数都是大于 \(1\) 但不为 \(2\) 的,同时保证了 \(3\)\(2\) 的度数合法,故这是可行的。

Part2

下图是另一结构,\(6, 7, 8\) 是树上的点,且 \(6, 7\) 是叶子。

image

考虑删掉 \(3, 5\),如下构造,选择 \((3, 2)\)\((2, 5)\) 即可。

image

容易检验构造的合法性,每个点最终都不是二度的。

Part3

最后如何实现删点,一个简单的做法是每次删叶子。

  1. 如果当前叶子两侧没有缩起来的三角形,就把改叶子对应的三角形缩起来。
  2. 如果当前叶子一侧没有缩起来的三角形,如 Part1 构造可以删去。
  3. 如果当前叶子两侧都有缩起来的三角形,如 Part2 构造,先删去两侧的三角形再回到第一种情况。

注意到边界情况是 \(4\) 个点的图,此时删二度点没有意义,因为无法把未满足限制的点交给下一次删点处理。

因为点数少,爆搜或讨论即可。

容易知道 \(n = 3\) 时恰好无解,其余的均有解,事实上边界情况是一定有解的。

构造本身说明正确性。

dp

原理简单明了,做树形 dp,记录点的度数信息转移,输出方案容易得到。

转移讨论 \(2\)\(1\) 作为生成树的边。

注意边界情况是根的转移要选 \(2\) 条边。

实现

构造

#include <bits/stdc++.h>
using namespace std;
constexpr int N = 5e5 + 5;
int n;
set<int> e[N];
vector<int> ans[N];
map<int, int> tag[N];
queue<int> Q;
void link(int u, int v) {
    e[u].insert(v);
    e[v].insert(u);
}
void cut(int u, int v) {
    e[u].erase(v);
    e[v].erase(u);
}
void update(int u) {
    if (e[u].size() == 2) {
        Q.push(u);
    }
}
void mark(int u, int v, int w) {
    tag[u][v] = tag[v][u] = w;
}
void clear(int u, int v) {
    tag[u][v] = tag[v][u] = 0;
}
void fixed(int u, int v) {
    ans[u].push_back(v);
    ans[v].push_back(u);
}
struct Dsu {
    int cnt;
    vector<int> fa;
    Dsu(int n, int x) {
        init(n, x);
    }
    void init(int n, int x) {
        cnt = x;
        fa.assign(n + 5, 0);
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }
    int find(int x) {
        if (x == fa[x]) {
            return x;
        } else {
            return fa[x] = find(fa[x]);
        }
    }
    bool merge(int x, int y) {
        if (find(x) == find(y)) {
            return 0;
        }
        fa[find(x)] = find(y);
        cnt--;
        return 1;
    }
};
int main() {
    cin >> n;
    for (int i = 1; i < n; i++) {
        link(i, i + 1);
    }
    link(n, 1);
    for (int i = 1; i <= n - 3; i++) {
        int u, v;
        cin >> u >> v;
        link(u, v);
    }
    if (n == 3) {
        cout << "-1\n";
        return 0;
    }
    for (int i = 1; i <= n; i++) {
        if (e[i].size() == 2) {
            Q.push(i);
        }
    }
    for (int T = 1; T <= n - 4; T++) {
        int u = Q.front();
        Q.pop();
        int i = *e[u].begin(), j = *next(e[u].begin());
        int k = tag[u][i], l = tag[u][j];
        if (k == 0 && l != 0) {
            swap(i, j), swap(k, l);
        }
        if (k == 0 && l == 0) {
            mark(i, j, u);
        } else if (k != 0 && l == 0) {
            fixed(k, i), fixed(u, i);
        } else {
            fixed(u, k), fixed(u, l);
            mark(i, j, u);
        }
        cut(u, i), cut(u, j);
        clear(u, i), clear(u, j);
        update(i), update(j);
    }
    vector<pair<int, int>> edge;
    for (int i = 1; i <= n; i++) {
        for (int j : e[i]) {
            if (i < j) {
                edge.emplace_back(i, j);
            }
        }
        for (auto [j, k] : tag[i]) {
            if (i < j && k != 0) {
                edge.emplace_back(i, k);
                edge.emplace_back(j, k);
            }
        }
    }
    int m = edge.size();
    int cnt = 0;
    set<int> tmp;
    for (int i = 0; i < m; i++) {
        tmp.insert(edge[i].first);
        tmp.insert(edge[i].second);
    }
    cnt = tmp.size();
    for (int i = 0; i < 1 << m; i++) {
        Dsu dsu(n, cnt);
        bool flag = 1;
        vector<int> deg(n + 5, 0);
        for (int j = 0; j < m && flag; j++) {
            if (i >> j & 1) {
                if (dsu.merge(edge[j].first, edge[j].second)) {
                    deg[edge[j].first]++;
                    deg[edge[j].second]++;
                } else {
                    flag = 0;
                }
            }
        }
        if (dsu.cnt != 1) {
            flag = 0;
        }
        for (int i = 1; i <= n && flag; i++) {
            if (deg[i] == 2) {
                flag = 0;
            }
        }
        if (flag) {
            for (int j = 0; j < m; j++) {
                if (i >> j & 1) {
                    fixed(edge[j].first, edge[j].second);
                }
            }
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j : ans[i]) {
            if (i < j) {
                cout << i << " " << j << "\n";
            }
        }
    }
    return 0;
}

dp

yxh

posted @ 2023-08-06 19:22  olriutre  阅读(39)  评论(0)    收藏  举报