20241115

forgotmyhandle發表於2024-11-17

T1

自閉

題目條件可以擴充套件到任意矩形的四個頂點。則整個矩陣僅由第一行和第一列決定。容易發現最左上角的格子直接填 \(0\) 是一定合法的,因此只需要判斷是否存在陣列 \(a_i, b_i\) 滿足 \(A_{i, j} = a_i + b_j\) 即可。考慮將給出的限制視為邊,\(a_i, b_j\) 視為點建圖,顯然不同連通塊之間沒有任何關係。對於每個連通塊,只需要任選一個點令其值為 \(0\),然後求出這個連通塊中所有點的取值,然後只需要這個連通塊中的 \(\min a_i + \min b_j \ge 0\),這個連通塊就合法。當且僅當所有連通塊都合法,原圖合法。使用 bfs,複雜度線性。

程式碼
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
int n, m, K;
int head[200005], nxt[400005], to[400005], ew[400005], ecnt;
void add(int u, int v, int ww) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, ew[ecnt] = ww; }
int dist[200005];
bool vis[200005];
queue<int> q;
int main() {
    freopen("zibi.in", "r", stdin);
    freopen("zibi.out", "w", stdout);
    cin >> n >> m >> K;
    for (int i = 1; i <= K; i++) {
        int x, y, w;
        cin >> x >> y >> w;
        add(x, y + n, w);
        add(y + n, x, w);
    }
    for (int i = 1; i <= n; i++) {
        if (vis[i]) 
            continue;
        q.push(i);
        vis[i] = 1;
        int amn = 0, bmn = 2147483647;
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = nxt[i]) {
                int v = to[i];
                if (!vis[v]) {
                    vis[v] = 1;
                    dist[v] = ew[i] - dist[x];
                    q.push(v);
                    if (v <= n) 
                        amn = min(amn, dist[v]);
                    else 
                        bmn = min(bmn, dist[v]);
                } else if (dist[v] != ew[i] - dist[x]) {
                    cout << "Zikai\n";
                    return 0;
                }
            }
            if (bmn + amn < 0) {
                cout << "Zikai\n";
                return 0;
            }
        }
    }
    cout << "Zibi\n";
    return 0;
}

T2

先二分答案,然後考慮 \(dp_i\) 表示用前 \(i\) 條狗最多能覆蓋到哪一根骨頭。這個 dp 和那個 Lanterns 很像,就是當前狗向前,或當前狗向後,或者當前狗向前且前一條狗向後。分別判斷轉移條件進行轉移即可。

程式碼
#include <iostream>
#include <string.h>
using namespace std;
int n, dogcount, bone;
string str;
int lb[1000005], rb[1000005];
int ld[1000005], rd[1000005];
int id[1000005];
int dp[1000005], pmx[1000005];
bool chk(int k) {
    int mx = 0;
    memset(dp, 0, sizeof dp);
    memset(pmx, 0, sizeof pmx);
    for (int i = 1; i <= n; i++) {
        if (str[i] != 'D') 
            continue;
        if (mx >= lb[i]) 
            dp[i] = max(dp[i], lb[min(n, i + k)]);
        if (i <= k + 1 || dp[ld[i - 1]] >= lb[i - k - 1]) 
            dp[i] = max(dp[i], lb[i]);  
        if (i >= k + 1) {
            if (ld[i - 1] && ld[i - 1] >= i - k) {
                if (ld[i - 1] == rd[i - k]) {
                    if (dp[ld[i - k - 1]] >= lb[i - k - 1]) 
                        dp[i] = max(dp[i], max(dp[ld[i - 1]], lb[min(n, ld[i - 1] + k)]));
                } else {
                    if (dp[rd[i - k]] >= lb[i - k - 1])
                        dp[i] = max(dp[i], max(dp[ld[i - 1]], lb[min(n, ld[i - 1] + k)]));
                }
            }
        } else if (ld[i - 1])
            dp[i] = max(dp[i], lb[min(n, ld[i - 1] + k)]);
        pmx[i] = max(dp[i], pmx[ld[i - 1]]);
        mx = max(mx, dp[i]);
    }
    return pmx[ld[n]] >= lb[n];
}
int main() {
    freopen("dogs.in", "r", stdin);
    freopen("dogs.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> str;
    str = ' ' + str;
    for (int i = 1; i <= n + 1; i++) lb[i] = (str[i] == 'B' ? i : lb[i - 1]), ld[i] = (str[i] == 'D' ? i : ld[i - 1]), id[i] = id[i - 1] + (str[i] == 'B');
    rd[n + 1] = rb[n + 1] = n + 1;
    for (int i = n; ~i; i--) rd[i] = (str[i] == 'D' ? (++dogcount, i) : rd[i + 1]), rb[i] = (str[i] == 'B' ? (++bone, i) : rb[i + 1]);
    if (dogcount == 1) {
        int pos = 0;
        for (int i = 1; i <= n; i++) {
            if (str[i] == 'D') 
                pos = i;
        }
        int a = 0, b = pos, c = 0, d = pos;
        for (int i = pos; i; i--) str[i] == 'B' ? (++a, b = i) : 0;
        for (int i = pos; i <= n; i++) str[i] == 'B' ? (++c, d = i) : 0;
        if (a > c) 
            cout << a << " " << pos - b << "\n";
        else if (a < c) 
            cout << c << " " << d - pos << "\n";
        else 
            cout << a << " " << min(pos - b, d - pos) << "\n";
        return 0;
    }
    int l = 1, r = n, mid, ans = -1;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (chk(mid)) 
            ans = mid, r = mid - 1;
        else 
            l = mid + 1;
    }
    cout << bone << " " << ans << "\n";
    return 0;
}

T3

字串距離

瞎搜,加點剪枝就行了。退火也行。為了提高爆搜正確性,可以考慮對每一個位置先填這一位上的眾數,然後按照出現次數列舉填其他字元,再按照眾數出現次數從大往小填每一位。這樣搜加卡時即可透過。

程式碼
#include <iostream>
#include <algorithm>
#include <time.h>
using namespace std;
int n, m, d, mx;
string str[1005];
string cur = " ";
int dist[1005];
int cnt[20005][30];
int p[20005][30], q[20005];
void dfs(int y) {
    if (1.0 * clock() / CLOCKS_PER_SEC >= 0.95) {
        cout << "-1\n";
        exit(0);
    }
    if (y == m + 1) {
        cout << cur.substr(1) << "\n";
        exit(0);
    }
    int x = q[y];
    // cout << y << " " << x << " a\n";
    for (int _ = 0; _ < 26; _++) {
        if (cnt[x][p[x][_]] == n) 
            continue;
        int i = p[x][_];
        // cout << _ << " " << i << " b\n";
        bool ok = 1;
        for (int j = 1; j <= n; j++) {
            if (dist[j] + (i != str[j][x] - 'a') > d) {
                ok = 0;
                break;
            }
        }
        if (ok) {
            for (int j = 1; j <= n; j++) dist[j] += (i != str[j][x] - 'a');
            cur[x] = i + 'a';
            dfs(y + 1);
            for (int j = 1; j <= n; j++) dist[j] -= (i != str[j][x] - 'a');
        }
    }
}
int main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    cin >> n >> m >> d;
    for (int i = 1; i <= m; i++) {
        cur += ' ', q[i] = i;
        for (int j = 0; j < 26; j++) cnt[i][j] = n;
    }
    for (int i = 1; i <= n; i++) {
        cin >> str[i];
        str[i] = ' ' + str[i];
        for (int j = 1; j <= m; j++) --cnt[j][str[i][j] - 'a'];
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 0; j < 26; j++) p[i][j] = j;
        sort(p[i], p[i] + 26, [i](int a, int b) { return cnt[i][a] < cnt[i][b]; });
    }
    sort(q + 1, q + m + 1, [](int a, int b) { return cnt[a][p[a][0]] < cnt[b][p[b][0]]; });
    dfs(1);
    cout << "-1\n";
    return 0;
}

T4

獨立集

莫名其妙的構造題。先把所有邊從小的指向大的,只需要對每個點 \(x\) 找出一個集合 \(S(x)\),使得 存在一條從沒有入度的點走到 \(x\) 的路徑的長度模 \(k\)\(i \iff i \in S(x)\),然後找到一個 \(c\) 使得 \(c \in S(x) \land (c \bmod k + 1) \notin S(x)\),然後把 \(x\) 染成顏色 \(c \bmod k + 1\) 即可。可以發現這是對的。

程式碼
#include <iostream>
#include <vector>
using namespace std;
int n, m, k;
int clr[1000005], cnt[1000005];
vector<int> vec[1000005];
int main() {
    freopen("set.in", "r", stdin);
    freopen("set.out", "w", stdout);
    cin >> n;
    cin >> n >> m >> k;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        vec[max(u, v)].emplace_back(min(u, v));
    }
    for (int i = 1; i <= n; i++) {
        if (vec[i].empty()) 
            clr[i] = 1;
        else {
            for (int v : vec[i]) cnt[clr[v]]++;
            for (int v : vec[i]) {
                if (!cnt[clr[v] % k + 1]) {
                    clr[i] = clr[v] % k + 1;
                    break;
                }
            }
            for (int v : vec[i]) cnt[clr[v]]--;
        }
    }
    for (int i = 1; i <= n; i++) cout << clr[i] << " ";
    cout << "\n";
    return 0;
}

相關文章