HDU2024 R4

forgotmyhandle發表於2024-07-31

T4

先把四組分成兩組,左邊兩組叫 \(L\),右邊兩組叫 \(R\)。直接爆搜每個數屬於 \(L\) 還是 \(R\),過程中順帶 \(01\) 揹包出兩組分別能取到哪些子集異或和。接下來不妨設 \(w_{f(A)} \ge w_{f(B)}\)\(w_{f(C)} \ge w_{f(D)}\)。若 \(w_{f(A)} \ge w_{f(C)}\),則答案為 \(w_{f(A)} - w_{f(D)}\),否則答案為 \(w_{f(C)} - w_{f(B)}\)。按 \(w\) 升序列舉 \(f(A)\)\(f(C)\),則 \(f(B)\)\(f(D)\) 只需要異或一下。列舉的時候把 \(f(A)\)\(f(C)\) 一起列舉,順便記錄 \(f(B)\)\(f(D)\) 的字首最大值。

程式碼
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, S;
int a[20];
int w[2005];
int p[2005];
bool f[2][20][2005];
int ans = 0;
void dfs(int x, int sl, int sr) {
    if (x == n + 1) {
        for (int j = 0, ml = -1, mr = -1; j <= S; j++) {
            int i = p[j];
            if (f[0][x][i] && w[i] >= w[sl ^ i]) {
                ml = max(ml, w[sl ^ i]);
                if (mr != -1) 
                    ans = min(ans, w[i] - min(w[sl ^ i], mr));
            }
            if (f[1][x][i] && w[i] >= w[sr ^ i]) {
                mr = max(mr, w[sr ^ i]);
                if (ml != -1) 
                    ans = min(ans, w[i] - min(w[sr ^ i], ml));
            }
        }
        return;
    }
    for (int i = 0; i <= S; i++) {
        f[0][x + 1][i] = (f[0][x][i] | f[0][x][i ^ a[x]]);
        f[1][x + 1][i] = f[1][x][i];
    }
    dfs(x + 1, sl ^ a[x], sr);
    for (int i = 0; i <= S; i++) {
        f[0][x + 1][i] = f[0][x][i];
        f[1][x + 1][i] = f[1][x][i] | f[1][x][i ^ a[x]];
    }
    dfs(x + 1, sl, sr ^ a[x]);
}
int main() {
    int tc;
    cin >> tc;
    while (tc--) {
        ans = 2147483647;
        cin >> n >> m;
        S = (1 << m) - 1;
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 0; i <= S; i++) cin >> w[i], p[i] = i;
        sort(p, p + S + 1, [](int a, int b) { return w[a] < w[b]; });
        for (int i = 0; i <= S; i++) f[0][2][i] = f[1][2][i] = 0;
        f[0][2][a[1]] = f[0][2][0] = f[1][2][0] = 1;
        dfs(2, a[1], 0);
        cout << ans << "\n";
    }
    return 0;
}

T12

按照平面直角座標系第一象限建系。

先預處理出 \(pre[i]\) 表示從起點到達 \((i, p_i)\) 的最大收益,\(suf[i]\) 表示從終點到 \((i, p_i)\) 的最大收益。然後考慮對於一個詢問,肯定是從起點到達其左上或右下的一個點,然後從這個點到終點。考慮左上,則右下同理。若區域記憶體在至少一個點,則這個區域的答案就是 \(\max\limits_{(i, p_i) 在區域內} \{ pre[i] + suf[i] \}\)。如果區域內不存在一個點,那就可以強制令路徑經過這區域內的某個點。所以我們考慮對每個詢問在左上右下各建一個虛點(建在整個地圖的左上和右下應該也行),然後令這些虛點的權值為 \(0\),正常求它們的 \(pre\)\(suf\),然後查詢就可以變成二維數點。然後整個題就只需要一堆二維數點即可。

程式碼
#include <iostream>
#include <algorithm>
#include <math.h>
#define int long long
#define lowbit(x) ((x) & (-(x)))
using namespace std;
bool bg;
int n, m;
struct node {
    int x, y, v;
} a[1200005], b[2000005];
int pre[1200005];
int suf[1200005];
struct PBIT {
    int bit[900005];
    void add(int x, int y) { for (; x <= n + 10; x += lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        int ret = 0;
        for (; x; x -= lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
    void clear() { for (int i = 0; i <= n + 10; i++) bit[i] = 0; }
} pbit;
struct SBIT {
    int bit[900005];
    void add(int x, int y) { for (; x; x -= lowbit(x)) bit[x] = max(bit[x], y); }
    int query(int x) {
        // cerr << x << "qq\n";
        int ret = 0;
        for (; x <= n + 10; x += lowbit(x)) ret = max(ret, bit[x]);
        return ret;
    }
    void clear() { for (int i = 0; i <= n + 10; i++) bit[i] = 0; }
} sbit;
int ans[400005];
bool ed;
signed main() {
    // cout << (&ed - &bg) / 1024.0 / 1024.0 << "\n";
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int tc;
    cin >> tc;
    while (tc--) {
        cin >> n >> m;
        for (int i = 1; i <= m; i++) ans[i] = 0;
        for (int i = 1; i <= n; i++) {
            a[i].x = i;
            cin >> a[i].y >> a[i].v;
            ++a[i].x, ++a[i].y;
        }
        int bcnt = 0;
        for (int i = 1; i <= m; i++) {
            int x1, y1, x2, y2;
            cin >> x1 >> y1 >> x2 >> y2;
            swap(y1, y2);
            ++bcnt;
            b[bcnt].x = x1, b[bcnt].y = y1;
            ++b[bcnt].x, ++b[bcnt].y;
            a[++n] = (node) { b[bcnt].x - 1, b[bcnt].y + 1, 0 };
            --b[bcnt].x, ++b[bcnt].y;
            b[bcnt].v = i;

            ++bcnt;
            b[bcnt].x = x2, b[bcnt].y = y2;
            ++b[bcnt].x, ++b[bcnt].y;
            a[++n] = (node) { b[bcnt].x + 1, b[bcnt].y - 1, 0 };
            ++b[bcnt].x, --b[bcnt].y;
            b[bcnt].v = -i;
        }
        sort(a + 1, a + n + 1, [](node a, node b) { return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x); });
        for (int i = 1; i <= n; i++) {
            pre[i] = pbit.query(a[i].y) + a[i].v;
            pbit.add(a[i].y, pre[i]);
        }
        for (int i = n; i; i--) {
            suf[i] = sbit.query(a[i].y) + a[i].v;
            sbit.add(a[i].y, suf[i]);
            suf[i] -= a[i].v;
        }
        pbit.clear(), sbit.clear();
        for (int i = 1; i <= n; i++) b[++bcnt] = a[i], b[bcnt].v = n + i;
        sort(b + 1, b + bcnt + 1, [](node a, node b) { return a.x == b.x ? (a.v > b.v) : (a.x < b.x); });
        for (int i = 1; i <= bcnt; i++) {
            if (b[i].v > n) 
                sbit.add(b[i].y, pre[b[i].v - n] + suf[b[i].v - n]);
            else if (b[i].v > 0)
                ans[b[i].v] = max(ans[b[i].v], sbit.query(b[i].y));
        }
        sort(b + 1, b + bcnt + 1, [](node a, node b) { return a.x == b.x ? (a.v > b.v) : (a.x > b.x); });
        for (int i = 1; i <= bcnt; i++) {
            if (b[i].v > n) 
                pbit.add(b[i].y, pre[b[i].v - n] + suf[b[i].v - n]);
            else if (b[i].v < 0)
                ans[-b[i].v] = max(ans[-b[i].v], pbit.query(b[i].y));
        }
        for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
        pbit.clear(), sbit.clear();
    }
    return 0;
}