24/8/12 模擬賽

ccxswl發表於2024-08-12

hz暑假集訓 8/12

數字三角形

CF1517C
簽到題。

題意:
\(D\) 給你一個長度為 \(n\) 的排列 \(p\) ,你需要根據 \(p\) 構造出一個三角形。
該圖案需要滿足共 \(n\) 行,第 \(i\) 行有 \(i\) 個數,第 \(i\) 行最後一個數是 \(p_i\)
數值 \(p_i\)\(p_i\) 個且四聯通。
幾個位置是四聯通的,是指從其中某個位置出發,只往上下左右走,只經過這些位置,可以到達其中任意一個位置。

思路:
每個數向左擴充套件,然後向下擴充套件。

5 
5 1 4 2 3

5
5 1
5 4 4
5 4 3 3
5 4 3 2 2

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int maxN = 507;

int n, p[maxN];
int a[maxN][maxN];

void dfs(int x, int y, int num, int stp) {
    if (!stp) return;
    a[x][y] = num;
    if (!a[x][y - 1])
        dfs(x, y - 1, num, stp - 1);
    else
        dfs(x + 1, y, num, stp - 1);
}

int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> p[i];
    for (int i = 1; i <= n; i++)
        a[i][0] = -1;
    for (int i = 1; i <= n; i++)
        dfs(i, i, p[i], p[i]);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++)
            cout << a[i][j] << ' ';
        cout << '\n';
    }
}

那一天她離我而去

題意:無向圖找出從 1 到 1 的最小環長度。無重邊 無自環

思路:
首先考慮暴力,從 1 出發經歷一條邊到的每個點放到一個陣列,\(n^2\) 列舉起點終點跑最短路,再加上 1 到這兩個點的距離。

正解非常巧妙,二進位制拆分,分別考慮每一位,是 0 的看成起點,是 1 的看成終點,在建超級源點(niubi源點)連每一個起點,邊權為 $1 \to x $ 的邊權;每一個終點連超級匯點,邊權為 \(x \to 1\) 的邊權。跑最短路即可。
因為倆個不同的點二進位制下一定最少一位不同,所以每個點都會分別被分到起點,終點,所以不漏。

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int maxN = 1e4 + 7, maxM = 6e4;

struct node {
    int x, hx;
};
vector<node> D;

int head[maxN], tot;
struct edge {
    int ls, to, w;
} e[maxM << 1];
void add(int x, int y, int z, bool record) {
    if (record)
        D.push_back({x, head[x]});
    e[++tot] = {head[x], y, z};
    head[x] = tot;
}

void cancel() {
    while (!D.empty()) {
        auto tp = D.back();
        D.pop_back();
        tot--;
        head[tp.x] = tp.hx;
    }
}

int ans;
int n, m;

int dis[maxN];
bool vis[maxN];
priority_queue<pair<int, int>> q;
void dij(int S) {
    for (int i = 0; i <= n + 1; i++)
        dis[i] = 1e9, vis[i] = false;
    dis[S] = 0;
    q.push({0, S});
    while (!q.empty()) {
        int f = q.top().second;
        q.pop();
        if (vis[f]) continue;
        vis[f] = true;
        for (int i = head[f]; i; i = e[i].ls) {
            int to = e[i].to;
            if (dis[to] > dis[f] + e[i].w) {
                dis[to] = dis[f] + e[i].w;
                q.push({-dis[to], to});
            }
        }
    }
}

vector<pair<int, int>> A;

void solve() {
    cin >> n >> m;
    
    A.clear();
    ans = 1e9;
    tot = 0;
    for (int i = 0; i <= n + 1; i++)
        head[i] = 0;

    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        if (u > v) swap(u, v);
        if (u == 1)
            A.push_back({v, w});
        else {
            add(u, v, w, false);
            add(v, u, w, false);
        }
    }
    
    for (int i = 13; i >= 0; i--) {
        for (auto t : A) {
            int fi = t.first, se = t.second;
	        if ((fi >> i) & 1)
                add(0, fi, se, true);
            else 
                add(fi, n + 1, se, true);
        }
        dij(0);
        ans = min(ans, dis[n + 1]);

        cancel();
    }

    if (ans >= 1e9)
        cout << "-1\n";
    else
        cout << ans << '\n';
}

int main() {
    // freopen("leave.in", "r", stdin);
    // freopen("b.txt", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(nullptr);

    int T;
    cin >> T;
    while (T--)
        solve();
}

話說好像好多屆都考過這個題了, 網上有學哥的部落格。

哪一天她能重回我身邊

題意:有 \(n\) 張牌,牌正面是 \(a\),反面是 \(b\),初始都是正面。求最小的翻轉的次數,使牌上面的數互不相同,以及最少翻轉達成目標的方案數。

圖論題。。。
很巧妙的建圖方法,從每張牌的正面向反面連邊。發現方案合法為每個點出度小於等於 1 時。
翻轉相當於把有向邊翻轉。
然後發現對於一個聯通塊合法其實就是樹或基環樹。

對於樹,就可以考慮換根dp,設 \(g[x]\) 表示以 \(x\) 為根的最小翻轉數,那麼如果有一條邊是 \(x\to to\)\(g[to] = g[x] - 1\),如果是 \(to\to x\),就是 \(g[to] = g[x] + 1\)
每個聯通塊內的最小 \(g[x]\) 之和就是最總答案,第二問順便處理一下就好了。

對於基環樹,斷掉環上的一條邊跑樹的情況。最後分討一下邊的方向就可以了。

思路比較難想,程式碼我選擇賀(。

// 抄的學哥的
#include <bits/stdc++.h>

using namespace std;

#define int long long

const int maxN = 2e5 + 7, mod = 998244353;

int n;

struct edge {
    int ls, to, w, st;
} e[maxN << 1];
int head[maxN], tot;
void add(int u, int v, int w) {
    e[++tot] = {head[u], v, w, u};
    head[u] = tot;
}

int np, ne;
bool v[maxN], vi[maxN];
void dfs(int x, int f) {
    vi[x] = true;
    np++;
    for (int i = head[x]; i; i = e[i].ls) {
        ne++;
        if (e[i].to == f || vi[e[i].to]) continue;
        dfs(e[i].to, x);
    }
}
bool pd() {
    for (int i = 1; i <= n * 2; i++)
        if (!vi[i]) {
            np = ne = 0;
            dfs(i, 0);
            if (np < ne / 2)
                return true;
        }
    return false;
}
int f[maxN], S, T, ned;
void dfs1(int x, int fa) {
    v[x] = true;
    f[x] = 0;
    for (int i = head[x]; i; i = e[i].ls)
        if (e[i].to != fa) {
            if (!v[e[i].to]) {
                dfs1(e[i].to, x);
                f[x] += f[e[i].to] + e[i].w;
            }
            else
                S = e[i].st, T = e[i].to, ned = i;
        }
}
int g[maxN];
vector<int> tem;
void dfs2(int x, int fa) {
    tem.push_back(g[x]);
    for (int i = head[x]; i; i = e[i].ls)
        if (e[i].to != fa && i != ned && i != (ned ^ 1)) {
            if (e[i].w) g[e[i].to] = g[x] - 1;
            else g[e[i].to] = g[x] + 1;
            dfs2(e[i].to, x);
        }
}

void solve() {
    tot = 1;
    memset(f, 0, sizeof(f));
    memset(g, 0, sizeof(g));
    memset(v, 0, sizeof(v));
    memset(vi, 0, sizeof(vi));
    memset(head, 0, sizeof(head));
    tem.clear();

    cin >> n;
    for (int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b, 1), add(b, a, 0);
    }
    if (pd())
        return cout << "-1 -1\n", void();
    
    int ans = 0;
    int minn = 0, ans2 = 1;
    for (int i = 1; i <= n * 2; i++)
        if (!v[i]) {
            S = T = ned = -1;
            tem.clear();
            minn = 0;
            dfs1(i, 0);
            g[i] = f[i];
            dfs2(i, 0);
            if (S == -1) {
                sort(tem.begin(), tem.end());
                for (unsigned j = 0; j < tem.size(); j++)
                    if (tem[j] == tem[0])
                        minn++;
                    else break;
                ans += tem[0];
            }
            else {
                ned %= 2;
                if (g[S] + ned == g[T] + (ned ^ 1))
                    minn = 2;
                else
                    minn = 1;
                ans += min(g[S] + ned, g[T] + (ned ^ 1));
            }
            ans2 = ans2 * minn % mod;
        }
    cout << ans << ' ' << ans2 << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);

    int Tim;
    cin >> Tim;
    while (Tim--)
        solve();
}

T4 不會