HDU2024 R4

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;
}

T6

实际上是两方各先走 \(m - k\) 步,然后自己再走 \(k\) 步。直接 dp,把当前轮数、我方坐标、敌方坐标和敌方血量全扔进状态里,发现爆了。但是观察发现两方同时移动,坐标差不会改变。因此敌方坐标就不用记了。但是对面撞墙了会使得坐标差偏离,但是偏离范围不超过 \(hp\),因此就把偏离量扔到状态里,就可以直接搞了。

代码
#include <iostream>
#include <string.h>
using namespace std;
const int P = 1000000007;
int n, m, k, h;
string str[55];
int cnt[55][55][55];
int dp[2][55][55][6][15][15];
int ans[55][55][55];
int sx, sy, ex, ey, dx, dy;
int dirx[4] = { 0, -1, 0, 1 };
int diry[4] = { -1, 0, 1, 0 };
inline void Madd(int& x, int y) { (x += y) >= P ? (x -= P) : 0; }
signed main() {
    int tc;
    cin >> tc;
    while (tc--) {
        memset(ans, 0, sizeof ans);
        cin >> n >> m >> k >> h;
        for (int i = 1; i <= n; i++) {
            cin >> str[i];
            str[i] = ' ' + str[i];
            for (int j = 1; j <= n; j++) {
                if (str[i][j] == 'P') 
                    sx = i, sy = j, str[i][j] = '.';
                else if (str[i][j] == 'E') 
                    ex = i, ey = j, str[i][j] = '.';
                for (int asdf = 0; asdf <= h; asdf++) {
                    for (int k = -h; k <= h; k++) {
                        for (int l = -h; l <= h; l++) 
                            dp[0][i][j][asdf][k + 5][l + 5] = dp[1][i][j][asdf][k + 5][l + 5] = 0;
                    }
                }
            }
        }
        dx = ex - sx, dy = ey - sy;
        dp[0][sx][sy][h][5][5] = 1;
        int cur = 0;
        m -= k;
        while (m--) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    for (int k = 0; k <= h; k++) {
                        for (int a = -h; a <= h; a++) {
                            for (int b = -h; b <= h; b++) 
                                dp[cur ^ 1][i][j][k][a + 5][b + 5] = 0;
                        }
                    }
                }
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    for (int k = 1; k <= h; k++) {
                        for (int a = -h + k; a <= h - k; a++) {
                            int lim = h - k - abs(a);
                            for (int b = -lim; b <= lim; b++) {
                                if (!dp[cur][i][j][k][a + 5][b + 5]) 
                                    continue;
                                for (int d = 0; d < 4; d++) {
                                    int x1 = i + dirx[d], y1 = j + diry[d];
                                    if (!x1 || !y1 || x1 > n || y1 > n || str[x1][y1] == '#') 
                                        continue;
                                    int x2 = x1 + dx + a, y2 = y1 + dy + b;
                                    if (!x2 || !y2 || x2 > n || y2 > n || str[x2][y2] == '#') 
                                        Madd(dp[cur ^ 1][x1][y1][k - 1][a - dirx[d] + 5][b - diry[d] + 5], dp[cur][i][j][k][a + 5][b + 5]);
                                    else 
                                        Madd(dp[cur ^ 1][x1][y1][k][a + 5][b + 5], dp[cur][i][j][k][a + 5][b + 5]);
                                }
                            }
                        }
                    }
                }
            }
            cur ^= 1;
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    for (int a = -h; a <= h; a++) {
                        for (int b = -h; b <= h; b++) 
                            Madd(ans[1][i][j], dp[cur][i][j][0][a + 5][b + 5]);
                    }
                }
            }
        }
        for (int i = 1; i <= k; i++) {
            for (int x = 1; x <= n; x++) {
                for (int y = 1; y <= n; y++) {
                    for (int d = 0; d < 4; d++) {
                        int tx = x + dirx[d], ty = y + diry[d];
                        if (tx > 0 && ty > 0 && tx <= n && ty <= n && str[tx][ty] == '.') 
                            Madd(ans[i + 1][tx][ty], ans[i][x][y]);
                    }
                }
            }
        }
        int Ans = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) 
                Madd(Ans, ans[k + 1][i][j]);
        }
        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) {
        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;
}

“分一半”的思想。

建虚点。

posted @ 2024-07-31 22:38  forgotmyhandle  阅读(30)  评论(0)    收藏  举报