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;
}
“分一半”的思想。
建虚点。

浙公网安备 33010602011771号