20240404

forgotmyhandle發表於2024-04-04

T1

洛谷 P3436 PRO-Professor Szu

首先縮點。然後從所有沒有入度的強連通分量開始 dfs,進行 dp,數一下每個點到終點有多少路徑。要注意的是當且僅當一個點能夠到達終點時才能夠用來更新其他點的 dp 值。

程式碼
#include <iostream>
#define int long long
using namespace std;
int n, m;
struct Graph {
    int head[1000005], nxt[1000005], to[1000005], ecnt;
    void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
} g1, g2;
int clr[1000005], scnt, ncnt;
int dfn[1000005], low[1000005], stk[1000005], sz;
bool vis[1000005];
int ssz[1000005];
void tarjan(int x) {
    dfn[x] = low[x] = ++ncnt;
    stk[++sz] = x;
    vis[x] = 1;
    for (int i = g1.head[x]; i; i = g1.nxt[i]) {
        int v = g1.to[i];
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if (vis[v]) 
            low[x] = min(low[x], dfn[v]);
    }
    if (low[x] == dfn[x]) {
        ++scnt;
        int t;
        do {
            t = stk[sz--];
            vis[t] = 0;
            clr[t] = scnt;
            ++ssz[scnt];
        } while (t != x);
    }
}
int in[1000005];
bool sl[1000005];
bool lp[1000005];
int dest;
int dp[1000005];
bool tol[1000005];
bool tod[1000005];
void dfs(int x) {
    if (vis[x]) 
        return;
    vis[x] = 1;
    tol[x] |= lp[x];
    for (int i = g2.head[x]; i; i = g2.nxt[i]) {
        int v = g2.to[i];
        dfs(v);
        tod[x] |= tod[v];
        if (tod[v]) {
            dp[x] += dp[v];
            tol[x] |= tol[v];
            dp[x] = min(dp[x], 36501ll);
        }
    }
}
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        g1.add(u, v);
        if (u == v) 
            sl[u] = 1;
    }
    for (int i = 1; i <= n + 1; i++) {
        if (!dfn[i]) 
            tarjan(i);
    }
    dp[dest = clr[n + 1]] = 1;
    tod[dest] = 1;
    for (int i = 1; i <= n + 1; i++) {
        for (int j = g1.head[i]; j; j = g1.nxt[j]) {
            int v = g1.to[j];
            if (clr[i] != clr[v]) 
                g2.add(clr[i], clr[v]), in[clr[v]]++;
        }
    }
    for (int i = 1; i <= n + 1; i++) lp[clr[i]] |= sl[i];
    for (int i = 1; i <= scnt; i++) lp[i] |= (ssz[i] > 1);
    for (int i = 1; i <= scnt; i++) {
        if (in[i] == 0) 
            dfs(i);
    }
    int ans = -1, acnt = 0;
    for (int i = 1; i <= scnt; i++) {
        if (dp[i]) {
            if (tol[i] || dp[i] > 36500) 
                dp[i] = 36501;
            if (dp[i] > ans) 
                ans = dp[i], acnt = 0;
            if (dp[i] == ans) 
                acnt += ssz[i] - (dest == i);
        }
    }
    if (ans > 36500) 
        cout << "zawsze\n";
    else 
        cout << ans << "\n";
    cout << acnt << "\n";
    for (int i = 1; i <= n; i++) {
        if (dp[clr[i]] == ans) 
            cout << i << " ";
    }
    cout << "\n";
    return 0;
}

T2

洛谷 P3438 ZAB-Frogs

首先二分答案,check 時 bfs,然後就要求每個點是否會被某個圓覆蓋。可以發現一個圓在每一列上覆蓋的都是一條線段,而且中點在圓心所在橫線上。又可以觀察到圓心橫座標離當前列越近,則覆蓋的線段越長,並且能夠覆蓋相同圓心縱座標的圓所覆蓋的線段。因此只需要對每列上的每個點求出在縱座標與之相等且橫座標最近的圓心即可。每次二分一個距離(的平方),就對每個點都算一下它給所在列貢獻了哪些被覆蓋的點。差分一下即可。有一個細節就是這樣做會使得原本距離正好為二分的距離的點被封掉不能走,所以這樣求出的答案會比標準答案小 \(1\)。輸出時加上即可。

程式碼
#include <iostream>
#include <queue>
#define int long long
using namespace std;
int sx, sy, tx, ty;
int n, m, X;
bool bad[1005][1005];
int a[1005][1005];
int b[1005][1005];
int d[1005][1005];
int s[1005][1005];
bool vis[1005][1005];
queue<pair<int, int> > q;
int dx[4] = { 1, 0, -1, 0 };
int dy[4] = { 0, 1, 0, -1 };
int srt[5000005];
bool bfs() {
    if (s[sx][sy]) 
        return 0;
    if (sx == tx && sy == ty) 
        return 1;
    while (!q.empty()) q.pop();
    q.push(make_pair(sx, sy));
    s[sx][sy] = 1;
    while (!q.empty()) {
        pair<int, int> tmp = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int xx = tmp.first + dx[i], yy = tmp.second + dy[i];
            if (xx && yy && xx <= n && yy <= m && !s[xx][yy]) {
                if (xx == tx && yy == ty) 
                    return 1;
                s[xx][yy] = 1;
                q.push(make_pair(xx, yy));
            }
        }
    }
    return 0;
}
bool chk(int k) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) 
            s[i][j] = 0;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (d[i][j] * d[i][j] > k) 
                continue;
            int t = k - d[i][j] * d[i][j];
            t = srt[t];
            s[max(1ll, i - t)][j]++;
            s[min(n + 1, i + t + 1)][j]--;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) 
            s[i][j] += s[i - 1][j];
    }
    return bfs();
}
signed main() {
    cin >> n >> m;
    cin >> sx >> sy >> tx >> ty;
    cin >> X;
    for (int i = 1; i * i <= 5000000; i++) {
        for (int j = i * i; j < min(5000000ll, (i + 1ll) * (i + 1)); j++) 
            srt[j] = i;
    }
    for (int i = 1; i <= X; i++) {
        int x, y;
        cin >> x >> y;
        bad[x][y] = 1;
    }
    for (int i = 1; i <= n; i++) {
        int lst = -10000;
        for (int j = 1; j <= m; j++) {
            if (bad[i][j]) 
                lst = j;
            a[i][j] = lst;
        }
        lst = n + 10000;
        for (int j = m; j; j--) {
            if (bad[i][j]) 
                lst = j;
            b[i][j] = lst;
            d[i][j] = min(j - a[i][j], b[i][j] - j);
        }
    }
    int l = 0, r = 5000000, mid, ans = -1;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (chk(mid)) 
            l = mid + 1, ans = mid;
        else 
            r = mid - 1;
    }
    cout << ans + 1 << "\n";
    return 0;
}

T3

洛谷 P3439 MAG-Warehouse

首先切比雪夫轉曼哈頓,將兩維獨立開。對每一維,發現答案選的點不在端點上上肯定不優,因為可以往左或往右移使得總答案變小。所以列舉一下所有點,求一個最小總距離。然後由於直接使用 double 算實在是有可能掉精度,所以把算出來的點在最後的時候除以 \(2\),然後波動 \(\pm 1\),就可以求出最小的點了。

切比雪夫距離轉曼哈頓距離:

\((x, y)\) 變成 \((\frac{x + y}{2}, \frac{x - y}{2})\),這樣變換之後兩點間的曼哈頓距離就是原本的切比雪夫距離。

程式碼
#include <iostream>
#include <algorithm>
#include <string.h>
#define abs(x) ((x) > 0 ? (x) : (-(x)))
#define int long long
using namespace std;
int n;
int xx[100005], yy[100005], t[100005];
pair<long double, int> x[100005], y[100005];
long double pre1[100005];
int pre2[100005];
int p1, p2;
long double cur;
int ax, ay;
int calc(int px, int py) {
    int ret = 0;
    for (int i = 1; i <= n; i++) ret += t[i] * max(abs(px - xx[i]), abs(py - yy[i]));
    return ret;
}
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> xx[i] >> yy[i] >> t[i];
        x[i].first = (xx[i] + yy[i]);
        y[i].first = (xx[i] - yy[i]);
        x[i].second = t[i];
        y[i].second = t[i];
    }
    sort(x + 1, x + n + 1);
    sort(y + 1, y + n + 1);
    cur = 0x7fffffffffffffff;
    for (int i = 1; i <= n; i++) pre1[i] = pre1[i - 1] + x[i].first * x[i].second;
    for (int i = 1; i <= n; i++) pre2[i] = pre2[i - 1] + x[i].second;
    for (int i = 1; i <= n; i++) {
        double tmp = x[i].first * pre2[i] - pre1[i] + pre1[n] - pre1[i] - x[i].first * (pre2[n] - pre2[i]);
        if (tmp < cur) 
            cur = tmp, p1 = x[i].first;
    }
    memset(pre1, 0, sizeof pre1);
    memset(pre2, 0, sizeof pre2);
    cur = 0x7fffffffffffffff;
    for (int i = 1; i <= n; i++) pre1[i] = pre1[i - 1] + y[i].first * y[i].second;
    for (int i = 1; i <= n; i++) pre2[i] = pre2[i - 1] + y[i].second;
    for (int i = 1; i <= n; i++) {
        double tmp = y[i].first * pre2[i] - pre1[i] + pre1[n] - pre1[i] - y[i].first * (pre2[n] - pre2[i]);
        if (tmp < cur) 
            cur = tmp, p2 = y[i].first;
    }
    int x1 = (p1 + p2) / 2, y1 = (p1 - p2) / 2;
    cur = 0x7fffffffffffffff;
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            int tmp;
            if ((tmp = calc(x1 + i, y1 + j)) < cur) 
                cur = tmp, ax = x1 + i, ay = y1 + j;
        }
    }
    cout << ax << " " << ay << "\n";
    return 0;
}