1. 程式人生 > 實用技巧 >【XR-2】傷痕

【XR-2】傷痕

不難發現,直接漫無目的地構造不是一個好的選擇,因為我們並不知道選擇四座城市方案的上界是什麼,因此下面可以來先分析一下這個方案的上界。

首先可以考慮這使得這四個點的匯出子圖是強連通的方案數,但是經過嘗試可以發現,合法的情況非常之多,因此我們來考慮不合法的情況。

從簡單的情況出發,當匯出子圖中不存在無向邊時:

  • 匯出子圖不強聯通當且僅當存在一個點在匯出子圖中出度或入度為 \(3\)

這種情況下不強聯通是顯然的,下面來證明其他情況都是強連通的。

不難發現當這張圖如果存在三個點強連通時,因為另一個點不可能全連出邊或入邊,因此這四個點必然四強聯通。

接下來證明不存在不出現三個點強連通的情況:

首先隨便選取一個點,找到它連向的點和連向它的點分別記作 \(A, B\)

,那麼此時如果是 \(A \rightarrow B\) 則已經產生了三元環,所以只能從 \(B \rightarrow A\),此時 \(B\) 出度已經為 \(2\) 那麼只能選擇讓另一個點連向 \(B\) ……

接下來通過這種必然性最終可以推出必然存在三個點強連通。

再來考慮存在無向圖的情況:

  • 同理,存在一個點在匯出子圖中出度或入度(有向邊)為 \(3\) 時一定不強連通。

  • 當只存在一條無向邊時,也必然能形成三聯通。

  • 當存在兩條無向邊且在端點處相交時,已經形成了三聯通。

  • 當存在兩條邊且不在端點處相交時,不難發現也易證得當且僅當所有有向邊都從一邊指向另一邊時不強連通。

  • 當存在三條或三條以上的無向邊時,已經形成了三聯通/四聯通。

因此,最終可以歸納得唯一不合法的兩種情況:

  1. 存在一個點連出去或被連入三條有向邊。

  2. 存在兩條無向邊邊且不在端點處相交時,所有有向邊都從一邊指向另一邊。

可以發現的是,當點多以後第一種情況是避免不了的,因為總是存在一個點向外連出多條邊。

那麼可以來分析一下第一種情況方案數的下界,令每個點連出去的邊數量為 \(A_i\)

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

那麼一個點連出去三條邊的不合法方案應為:\(\sum\limits_{i = 1} ^ n \dbinom{A_i}{3}\)

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

\[\sum\limits_{i = 1} ^ n \dbinom{A_i}{3} \ge n \times \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 \rightarrow B, A \rightarrow C\)

通過嘗試不難發現可以讓每個點連向右邊距離其 \(1 \sim \frac{n - 3}{2} - 1\) 的所有點,即可滿足所有 \(1.1, 1.2\) 的不合法狀態重合。

此時回過頭來又可以發現,對於任意一組不在端點相交的無向邊都必然不會存在 \(2\) 的不合法情況。

因此,此時合法的方案數達到了上界 \(\dbinom{n}{4} - n \times \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\n", 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;
}

當需要構造某種方案達到極值時,往往先分析極值的界限在基於這個界限進行構造。

同時,在構造時一定要抱有目的,按需構造。