10 收心赛1 T3 逻辑网络 题解
巡逻网络
题面
平面上,一开始有 \(n\) 个固定站点 \(a_1,a_2...a_n\) 和 \(m\) 个可选站点 \(b_1,b_2,...b_m\)。
小明要选出一些站点作为巡逻网络的节点。为了巡逻网络的高效,小明希望选出的节点之间两两曼哈顿距离之和尽可能大。
接下来有 \(q\) 次修改操作,每次加入一个点 \(p\) 作为新的可选站点。
另外有查询操作:选出所有的固定站点,再从当前所有的可选站点中选出 \(m\) 个可选站点,使得这 \(m + n\) 个点之间两两曼哈顿距离之和尽可能大,并输出这个最大的曼哈顿距离之和。
小明只在所有修改全部结束后询问你一次。
另外有 \(k\) 表示特殊性质,\(k = 1\) 表示只有一维,\(k = 2\) 表示有两维
\(1 \le k \le 2\)
\(0 \le n \le 20,\ 1 \le q \le 2 \times 10^4\)
\(2 \le m \le 4\)
曼哈顿距离:两个点 \((x_1,y_1)\) 和 \((x_2,y_2)\) 的曼哈顿距离 \(d(x,y)\) 为 \(|x_1 - x_2| + |y_1 - y_2|\)
题解
\(k = 1\)
这道题我们可以将这些点对答案的贡献分成类:固定点之间的距离,固定点和可选点之间的距离,可选点之间的距离
第一类的贡献是定值,可以直接预处理出来
第二类可以看做每个点的额外贡献,可以放在每个点上
所以我们要着重考虑第三类贡献如何计算
先来一个巧妙的转化
考虑两个点之间的距离计算,设第一个点的坐标为 \(a\) ,第二个点的坐标为 \(b\) ,两个点之间的距离为 \(|a - b|\)
实际上这个绝对值可以转化为最大值 \(max\{-a + b, -b + a\}\)
什么意思呢?我们可以假设放在后面的点的坐标一定比前面的点的坐标大,那么我们的答案就是后面点的坐标减去前面点的坐标,那么我们可以枚举两个点之间的位置关系,然后利用刚才的公式算个最大值即为我们想要的两个点间的距离
有什么用?原题中我们不是要选出 \(m\) 个点吗,这 \(m\) 个点之间的距离不好算,我们可以先规定一个位置关系,也就是枚举每个点放在哪个位置
假设我们已经选好了 \(m\) 个点,那么这个方案的贡献为
那我们可以将贡献分配到每个点上,设 \(f(i,S)\) 表示考虑到第 \(i\) 个点,已经选了一些点放在了位置集合为 \(S\) 的位置的最大贡献
转移:
时间复杂度为 \(O((m + q)2^mm)\) ,大概是 \(6e6\) ,可以过,\(calc(i)\) 可以预处理出来
\(k = 2\)
\(k = 2\) 的情况和 \(k = 1\) 的情况差不多,不过多了一维的限制,我们同样可以用类似的 \(dp\) 去做
设 \(f(i,S_1,S_2)\) 表示考虑前 \(i\) 个点,已经选的点的横坐标占据了 \(S_1\) 的位置,纵坐标占据了 \(S_2\)
转移和上面的差不多
时间复杂度 \(O((m + q)4^mm^2)\) 大概 \(8e7\) 有点勉强,因为选的点的数量是一定的,所以 \(S_1\) 和 \(S_2\) 中的 1 的数量也一定是一样的,所以实际的状态量为
这是 \(O(\frac {4^m} {\sqrt m})\) 的(不会证明)不过这样大概就是 \(4e7\) ,过掉没什么问题
code
代码不会写,给个标程跑路了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
#define popc(x) __builtin_popcount (x)
const int N = 2e4 + 50;
int n, m, _, t;
ll X[N], Y[N], X1[N], Y1[N];
ll f[N][1 << 4][1 << 4], c[N], sum;
int main () {
freopen ("net.in", "r", stdin);
freopen ("net.out", "w", stdout);
ios :: sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
cin >> m >> n >> _ >> t;
n += m;
//输入固定站点
for (int i = 1; i <= t; i++) {
if (_ == 2) cin >> X1[i] >> Y1[i];
else cin >> X1[i];
}
for (int i = 1; i <= n; i++) {
if (_ == 2) cin >> X[i] >> Y[i];
else cin >> X[i];
}
//计算固定点之间的贡献
for (int i = 1; i <= t; i++) {
for (int j = i + 1; j <= t; j++) {
sum += abs (X1[i] - X1[j]) + abs (Y1[i] - Y1[j]);
}
}
//计算 c 数组,c[i] 表示可选站 i 到所有固定站的距离贡献
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= t; j++) {
c[i] += abs (X[i] - X1[j]) + abs (Y[i] - Y1[j]);
}
}
//dp数组初始化
memset (f, -0x3f, sizeof f);
f[0][0][0] = 0;
int mn = (1 << m);
for (int i = 1; i <= n; i ++) {
for (int S1 = 0; S1 < mn; S1 ++) {
for (int S2 = 0; S2 < mn; S2 ++) {
if (popc (S1) != popc (S2)) continue;
//注意不要落情况
f[i][S1][S2] = f[i - 1][S1][S2]; //不选当前站点
for (int x = 0; x < m; x ++) { //选当前点
if (!((S1 >> x) & 1)) continue;
for (int y = 0; y < m; y ++) {
if (!((S2 >> y) & 1)) continue;
ll val = X[i] * ((x + 1) * 2 - m - 1) + Y[i] * ((y + 1) * 2 - m - 1) + c[i];
f[i][S1][S2] = max (f[i][S1][S2], f[i - 1][S1 ^ (1 << x)][S2 ^ (1 << y)] + val);
}
}
}
}
}
cout << sum + f[n][mn - 1][mn - 1] << endl;
return 0;
}

浙公网安备 33010602011771号