【XR-2】伤痕

不难发现,直接漫无目的地构造不是一个好的选择,因为我们并不知道选择四座城市方案的上界是什么,因此下面可以来先分析一下这个方案的上界。

首先可以考虑这使得这四个点的导出子图是强连通的方案数,但是经过尝试可以发现,合法的情况非常之多,因此我们来考虑不合法的情况。

从简单的情况出发,当导出子图中不存在无向边时:

  • 导出子图不强联通当且仅当存在一个点在导出子图中出度或入度为 (3)

这种情况下不强联通是显然的,下面来证明其他情况都是强连通的。

不难发现当这张图如果存在三个点强连通时,因为另一个点不可能全连出边或入边,因此这四个点必然四强联通。

接下来证明不存在不出现三个点强连通的情况:

首先随便选取一个点,找到它连向的点和连向它的点分别记作 (A, B),那么此时如果是 (A ightarrow B) 则已经产生了三元环,所以只能从 (B ightarrow A),此时 (B) 出度已经为 (2) 那么只能选择让另一个点连向 (B) ……

接下来通过这种必然性最终可以推出必然存在三个点强连通。

再来考虑存在无向图的情况:

  • 同理,存在一个点在导出子图中出度或入度(有向边)为 (3) 时一定不强连通。

  • 当只存在一条无向边时,也必然能形成三联通。

  • 当存在两条无向边且在端点处相交时,已经形成了三联通。

  • 当存在两条边且不在端点处相交时,不难发现也易证得当且仅当所有有向边都从一边指向另一边时不强连通。

  • 当存在三条或三条以上的无向边时,已经形成了三联通/四联通。

因此,最终可以归纳得唯一不合法的两种情况:

  1. 存在一个点连出去或被连入三条有向边。

  2. 存在两条无向边边且不在端点处相交时,所有有向边都从一边指向另一边。

可以发现的是,当点多以后第一种情况是避免不了的,因为总是存在一个点向外连出多条边。

那么可以来分析一下第一种情况方案数的下界,令每个点连出去的边数量为 (A_i)

可知 (sumlimits_{i = 1} ^ n A_i = frac{n(n - 1)}{2} - n = frac{n(n - 3)}{2})

那么一个点连出去三条边的不合法方案应为:(sumlimits_{i = 1} ^ n dbinom{A_i}{3})

因为 (dbinom{x}{3} = frac{x(x - 1)(x - 2)}{3})([3, + infty)) 上是凸函数,因此可知:

[sumlimits_{i = 1} ^ n dbinom{A_i}{3} ge n imes dbinom{frac{n - 3}{2}}{3} ]

等号成立当且仅当 (A_i = frac{n - 3}{2})

接下去貌似没法继续求得第二种情况方案数的下界了,可以考虑返回原问题尝试着构造一番。

因为每个点的对称性,所以我们分配无向边的时候最简单的方法就是让每个点分配得度数一样:都分配两条无向边与其相连。

不难发现,只需要将这个点连向正多边形中距离其最远的两个点即可。

接下来,每个点恰好剩下了 (frac{n - 3}{2}) 个入度。

那么此时可以发现,因为必然存在入度为 (3) 的点,所以不合法情况 (1.2) 会必然存在。

但是,我们可以让入度为 (3) 的点和出度为三的点尽可能出现在一起就可以减少不合法的情况。

也就是当三个点 (A, B, C) 连向同一个点 (D) 时,存在一个点不妨假设为 (A),满足 (A ightarrow B, A ightarrow C)

通过尝试不难发现可以让每个点连向右边距离其 (1 sim frac{n - 3}{2} - 1) 的所有点,即可满足所有 (1.1, 1.2) 的不合法状态重合。

此时回过头来又可以发现,对于任意一组不在端点相交的无向边都必然不会存在 (2) 的不合法情况。

因此,此时合法的方案数达到了上界 (dbinom{n}{4} - n imes dbinom{frac{n - 3}{2}}{3}),复杂度 (O(n ^ 2))

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 1e2 + 5;
int n, ans, a[N][N];
int read() {
    char c; int x = 0, f = 1;
    c = getchar();
    while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int Tr(int x, int d) { return (x + d) % n == 0 ? n : (x + d) % n;}
int main() {
    n = read();
    if(n >= 4) {
        ans = n * (n - 1) * (n - 2) * (n - 3) / 24;
        int k = (n - 3) / 2;
        if(k >= 3) ans -= n * k * (k - 1) * (k - 2) / 6;
    }
    if(n == 1) puts("0"), puts("0");
    else {
        printf("%d
", ans);
        rep(i, 1, n) a[i][Tr(i, n / 2)] = a[i][Tr(i, n / 2 + 1)] = 1;
        rep(i, 1, n) rep(j, 1, n / 2 - 1) a[i][Tr(i, j)] = 1;
        rep(i, 1, n) {
            rep(j, 1, n) printf("%d ", a[i][j]);
            puts("");
        }
    }
    return 0;
}

当需要构造某种方案达到极值时,往往先分析极值的界限在基于这个界限进行构造。

同时,在构造时一定要抱有目的,按需构造。

原文地址:https://www.cnblogs.com/Go7338395/p/13891182.html