梦熊 NOIP 十三连测模拟赛记录
题目大意
给定平面直角坐标系上的
个整点,求任意两个不同的点的曼哈顿距离与欧几里得距离的比的最大值,多组询问。
数据范围:, 。
思路分析
考虑我们就是要让连线段的角度最接近
按旋转后的
最终的时间复杂度为单组询问
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const long double pi = acos(-1.0);
int n;
pair<int, int> c[N];
int cmp1(pair<int, int> x, pair<int, int> y) {
return x.first + x.second < y.first + y.second;
}
int cmp2(pair<int, int> x, pair<int, int> y) {
return x.first - x.second < y.first - y.second;
}
long double calc() {
long double res = 0;
for (int i = 1; i < n; ++i) {
int x = abs(c[i].first - c[i + 1].first), y = abs(c[i].second - c[i + 1].second);
res = max(res, (x + y) / hypot((long double)x, (long double)y));
}
return res;
}
signed main() {
freopen("Apair.in", "r", stdin);
freopen("Apair.out", "w", stdout);
int T; scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d%d", &c[i].first, &c[i].second);
sort(c + 1, c + n + 1, cmp1);
long double res = calc();
sort(c + 1, c + n + 1, cmp2);
printf("%.20Lf\n", max(res, calc()));
}
return 0;
}
题目大意
有两个长度为
的数组 和 ,初始时 给定, 均为 ,每次可以选择数组 的前缀或者是后缀加 ,花费为选中元素个数,求最少花费使得 均不小于 ,多组询问。
数据范围:, 。
思路分析
考虑最终花费即为
也就是说,我们要找到一个“可生成的”序列
不妨将
如何去刻画一个序列“可被生成”呢?注意到,如果
在这些操作进行完后,必然有
考虑用一个简洁的式子去概括,考虑最终
考虑去除
现在我们得到了简洁的条件,如果初始的
记
建立出
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
pair<int, int> f[20][N];
int query(int l, int r) {
int k = log2(r - l + 1);
return max(f[k][l], f[k][r - (1 << k) + 1]).second;
}
vector< pair<int, int> > bt;
void dfs(int l, int r) {
if (l > r) return;
if (l == r) {
bt.push_back({1, min(a[l - 1], a[r + 1]) - a[l]});
return;
}
int m = query(l, r);
dfs(l, m - 1), dfs(m + 1, r);
bt.push_back({r - l + 1, min(a[l - 1], a[r + 1]) - a[m]});
}
signed main() {
freopen("Dmagic.in", "r", stdin);
freopen("Dmagic.out", "w", stdout);
int T; scanf("%d", &T);
while (T--) {
int n; scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
f[0][i] = make_pair(a[i], i);
}
for (int i = 1; (1 << i) <= n; ++i) {
for (int j = 1; j + (1 << i) - 1 <= n; ++j) {
f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
}
}
bt.clear();
a[0] = a[n + 1] = 1e9 + 1;
dfs(1, n);
long long s = 0, res = 0;
for (int i = 0; i <= n; ++i) s += abs(a[i] - a[i + 1]);
for (int i = 1; i <= n; ++i) res += a[i];
s -= (int)2e9 + 2;
sort(bt.begin(), bt.end());
reverse(bt.begin(), bt.end());
s >>= 1;
while (s > 0) {
long long t = min(s, (long long)bt.back().second);
s -= t, bt.back().second -= t, res += t * bt.back().first;
if (!bt.back().second) bt.pop_back();
}
printf("%lld\n", res);
}
return 0;
}
魔法阵
题目大意
给定
,求有多少个为 ( 任意)的矩阵 满足每个元素均 且主对角线上元素和不超过 ,且任意选 个不同行同列的元素,这 种选法元素的和相等,对 取模,多组询问。
数据范围:, 。
思路分析
不妨枚举
但是这样
问题转化为了对
对
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, P = 998244353;
int fac[N], iFac[N];
int C(int n, int m) {
if (!n) return !m;
if (m < 0) return 0;
int a = n + m - 1, b = n - 1;
return 1ll * fac[a] * iFac[a - b] % P * iFac[b] % P;
}
signed main() {
freopen("magic.in", "r", stdin);
freopen("magic.ans", "w", stdout);
for (int i = fac[0] = iFac[0] = 1; i < N; ++i) {
fac[i] = 1ll * fac[i - 1] * i % P;
iFac[i] = (i == 1 ? 1 : 1ll * (P - P / i) * iFac[P % i] % P);
}
for (int i = 1; i < N; ++i) iFac[i] = 1ll * iFac[i] * iFac[i - 1] % P;
int T; scanf("%d", &T);
while (T--) {
int n, m, res = 0; scanf("%d%d", &n, &m);
for (int k = 1; k * m <= n; ++k) {
res = ((long long)res + C(k + k + 1, n - k * m) - C(k + k + 1, n - k * m - k) + P) % P;
}
printf("%d\n", res);
}
return 0;
}
题目大意
给定一棵
个点的树,每个点上有一个按钮,初始时小 A 在 号节点,想要到达 号节点,每次你可以按一个非小 A 所处位置上的未被按过的按钮,小 A 会沿着最短路径走过一条边,求在所有点都按过一次的情况下,被小 A 经过至少一次的点的个数的最小值或说明无解。
数据范围:, 。
思路分析
对树黑白染色,显然一次操作会让小 A 到另一个颜色的点上去,所以如果
否则,我们先考虑小 A 能否只经过
具体地,假设路径上的第
解释一下这个公式,前面先直接走到这个节点,再左右横跳留够足够的步数走到终点。
若上述条件对于所有
否则,我们证明不符合这个条件的
考虑最终答案所呈现的连通块,此时仍然可以计算出更改后的
假设
我们考虑使用 dfs 从
如果不存在绝对众数,那么我们就按照子树从大到小的顺序剥离子节点(此时必然没有向下递归的必要),这是因为在子节点继续递归来减小对应子节点的
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
vector<int> G[N];
int n, dep[N], hv[N], siz[N];
void dfs(int x, int fa = -1) {
hv[x] = (x == n);
for (auto v : G[x]) if (v != fa) {
dep[v] = dep[x] + 1, dfs(v, x), hv[x] |= hv[v];
}
}
vector< pair<int, int> > tp;
int getC(int x) {
int sz = 1;
for (auto v : G[x]) if (dep[v] > dep[x]) {
sz += getC(v);
}
siz[x] = sz;
if (hv[x]) {
tp.push_back(make_pair(x, sz));
sz = 0;
}
return sz;
}
int lim, res;
void calc(int x) {
++res;
int w = 0;
for (auto v : G[x]) {
if (!hv[v] && dep[v] > dep[x] && siz[v] > siz[w]) {
w = v;
}
}
if (siz[w] + siz[w] >= siz[x]) {
if (siz[w] - (siz[x] - siz[w]) <= lim) return;
lim += (siz[x] - siz[w]);
calc(w);
} else {
vector<int> p;
for (auto v : G[x]) {
if (!hv[v] && dep[v] > dep[x]) {
p.push_back(siz[v]);
}
}
sort(p.rbegin(), p.rend());
int t = -siz[x];
for (auto x : p) {
t += (x << 1);
if (t >= -lim) break;
++res;
}
}
}
signed main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d", &n);
for (int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
G[x].push_back(y), G[y].push_back(x);
}
dfs(1);
int d = dep[n];
if ((n - d) & 1) return 0 & puts("-1");
getC(1);
reverse(tp.begin(), tp.end());
assert((int)tp.size() == d + 1);
int ok = -1;
for (int i = 0; i <= d; ++i) {
if (tp[i].second > ((n - d) >> 1) + i) assert(!~ok), ok = i;
}
if (!~ok) return 0 & printf("%d\n", d + 1);
lim = n - d + ok + ok - tp[ok].second;
calc(tp[ok].first);
printf("%d\n", d + 1 + res);
return 0;
}
树
题目大意
有一棵有根树,初始时只有一个根节点,你需要支持
次操作,每次操作为在一个节点上挂一个叶子,或询问一个子树的自同构数量对 取模后的结果。
数据范围:, 。
思路分析
考虑记
考虑如何动态维护
取
如何快速更新集合呢?我们发现一个点到根的路径经过的轻边只有
轻边是容易更新的,我们现在只要找到所有会改变的
发现一个必要条件是
总时间复杂度
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5, P = 998244353;
struct Mod {
long long m, p;
void init(int pp) { m = ((__int128)1 << 64) / pp; p = pp; }
long long operator ()(long long x) {
return x - ((__int128(x) * m) >> 64) * p;
}
} mod;
int ksm(int x, int y = P - 2) {
int res = 1;
while (y) {
if (y & 1) res = mod(1ll * res * x);
x = mod(1ll * x * x);
y >>= 1;
}
return res;
}
vector<int> G[N];
int n = 1, opt[N], id[N], inv[N];
int dep[N], fa[N], siz[N], son[N], top[N], dfn[N], rev[N], lst[N];
void dfs1(int x) {
siz[x] = 1;
for (auto v : G[x]) {
dep[v] = dep[x] + 1, fa[v] = x;
dfs1(v);
siz[x] += siz[v];
(siz[v] > siz[son[x]]) && (son[x] = v);
}
}
void dfs2(int x) {
dfn[x] = ++dfn[0], lst[top[x]] = x, rev[dfn[0]] = x;
if (!son[x]) {
return;
}
top[son[x]] = top[x];
dfs2(son[x]);
for (auto v : G[x]) {
if (v != son[x]) dfs2(top[v] = v);
}
}
namespace DS1 {
int s[N];
void Init() {
for (int i = 1; i <= n; ++i) s[i] = 1;
}
int query(int x) {
int res = 1;
for (; x; x -= x & -x) res = 1ll * res * s[x] % P;
return res;
}
int query(int l, int r) {
return 1ll * query(r) * ksm(query(l - 1)) % P;
}
void modify(int x, int y) {
for (; x <= n; x += x & -x) s[x] = 1ll * s[x] * y % P;
}
}
vector<int> ids;
namespace DS4 {
int mx[1 << 20], lz[1 << 20];
void modify(int k, int l, int r, int x, int y, int v) {
if (l > y || r < x) return;
if (l >= x && r <= y) {
mx[k] += v, lz[k] += v;
return;
}
if (lz[k]) {
mx[k << 1] += lz[k];
lz[k << 1] += lz[k];
mx[k << 1 | 1] += lz[k];
lz[k << 1 | 1] += lz[k];
lz[k] = 0;
}
int mid = (l + r) >> 1;
modify(k << 1, l, mid, x, y, v);
modify(k << 1 | 1, mid + 1, r, x, y, v);
mx[k] = max(mx[k << 1], mx[k << 1 | 1]);
}
void query(int k, int l, int r, int x, int y) {
if (l > y || r < x || mx[k] <= 0) return;
if (l == r) {
ids.push_back(rev[l]);
return;
}
if (lz[k]) {
mx[k << 1] += lz[k];
lz[k << 1] += lz[k];
mx[k << 1 | 1] += lz[k];
lz[k << 1 | 1] += lz[k];
lz[k] = 0;
}
int mid = (l + r) >> 1;
query(k << 1, l, mid, x, y);
query(k << 1 | 1, mid + 1, r, x, y);
}
void active(int x) {
while (x) {
int z = top[x];
modify(1, 1, n, dfn[z] + 1, dfn[x], -1);
if (son[x]) modify(1, 1, n, dfn[son[x]], dfn[son[x]], 1);
x = fa[z];
}
}
}
void getPath(int x) {
while (x > 1) {
if (top[x] != 1) {
ids.push_back(top[x]);
}
DS4::query(1, 1, n, dfn[top[x]] + 1, dfn[x]);
x = fa[top[x]];
}
}
mt19937 rnd;
int vl[N];
namespace DS3 {
int p;
pair<int, int> z[1 << 20];
void Init() {
for (p = 1; p < n + 2; p <<= 1);
for (int i = 1; i <= p + n; ++i) z[i] = {1, 0};
}
pair<int, int> operator + (pair<int, int> x, pair<int, int> y) {
return {mod(1ll * x.first * y.first), mod((long long)x.first * y.second + x.second)};
}
void modifyK(int x, int y) {
for (z[x + p].first = (long long)z[x + p].first * y % P, x = (x + p) >> 1; x; x >>= 1) {
z[x] = z[x << 1] + z[x << 1 | 1];
}
}
void modify(int x, int y, int w) {
for (z[x + p] = {y, w}, x = (x + p) >> 1; x; x >>= 1) {
z[x] = z[x << 1] + z[x << 1 | 1];
}
}
int query(int l, int r) {
pair<int, int> L = make_pair(1, 0), R = make_pair(1, 0);
for (l += p - 1, r += p + 1; l ^ r ^ 1; ) {
if (~l & 1) L = L + z[l ^ 1];
if (r & 1) R = z[r ^ 1] + R;
l >>= 1, r >>= 1;
}
L = L + R;
return (L.first + L.second) % P;
}
int getHash(int x) {
int y = lst[top[x]];
int res = query(dfn[x], dfn[y]);
return res;
}
void active(int x) {
int tx = x; x = fa[x];
vector< pair<int, int> > tz;
while (x) {
int tp = top[x];
if (tp == 1) break;
int V = ksm(getHash(tp));
tz.push_back({dfn[fa[tp]], V});
x = fa[tp];
}
for (auto t : tz) {
modifyK(t.first, t.second);
}
x = tx;
modify(dfn[x], vl[dep[x]], vl[dep[x]]);
while (x) {
int tp = top[x];
if (tp == 1) break;
int V = getHash(tp);
modifyK(dfn[fa[tp]], V);
x = fa[tp];
}
}
}
unordered_map<int, int> M[N];
int usd[N];
void active(int x) {
DS3::active(x);
DS4::active(x);
}
int Fac[N], iFac[N];
signed main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
int q; scanf("%d", &q);
mod.init(P);
for (int i = 1, x; i <= q; ++i) {
scanf("%d%d", &opt[i], &x);
if (opt[i] == 0) G[x].push_back(id[i] = ++n);
else id[i] = x;
}
for (int i = Fac[0] = iFac[0] = 1; i <= n; ++i) {
Fac[i] = 1ll * Fac[i - 1] * i % P;
inv[i] = (i == 1 ? 1 : 1ll * (P - P / i) * inv[P % i] % P);
iFac[i] = 1ll * iFac[i - 1] * inv[i] % P;
}
dfs1(dep[1] = 1), dfs2(top[1] = 1);
for (int i = 1; i <= n; ++i) {
vl[i] = rnd() % P;
}
DS1::Init(), DS4::active(1), DS3::Init(), DS3::active(1);
for (int i = 1; i <= q; ++i) {
if (opt[i] == 0) {
usd[id[i]] = 1;
ids.clear();
getPath(fa[id[i]]);
for (auto z : ids) {
int val = DS3::getHash(z);
int ts = (usd[son[fa[z]]] && val == DS3::getHash(son[fa[z]]));
DS1::modify(dfn[fa[z]], 1ll * iFac[M[fa[z]][val] + ts] * Fac[M[fa[z]][val] + ts - 1] % P);
if (z != son[fa[z]]) {
--M[fa[z]][val];
}
}
active(id[i]);
ids.clear();
getPath(id[i]);
for (auto z : ids) {
int val = DS3::getHash(z);
int ts = (z != son[fa[z]] && usd[son[fa[z]]] && val == DS3::getHash(son[fa[z]]));
DS1::modify(dfn[fa[z]], 1ll * iFac[M[fa[z]][val] + ts] * Fac[M[fa[z]][val] + ts + 1] % P);
if (z != son[fa[z]]) {
++M[fa[z]][val];
}
}
} else {
printf("%d\n", DS1::query(dfn[id[i]], dfn[id[i]] + siz[id[i]] - 1));
}
}
return 0;
}
题目大意
给定长度为
的数组 ,每次可以选定正整数 ,并令 ,求最终可能得到的 的数量对 取模后的结果。
数据范围:, 。
思路分析
下文视
不妨令
如何判定一个
这样就可以 dp 了,先枚举第一条线段的长度
一般情况下,可以同样直接枚举第一条线段的长度(注意不要算重了),然后转移是类似的,只是层间的转移条件要稍微更改一下,时间复杂度是
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 505, P = 998244353;
int f[N][N], g[N][N];
signed main() {
freopen("mod.in", "r", stdin);
freopen("mod.out", "w", stdout);
int n; scanf("%d", &n);
vector<int> a;
for (int i = 1, x; i <= n; ++i) {
scanf("%d", &x), a.push_back(x);
}
sort(a.begin(), a.end()), a.erase(unique(a.begin(), a.end()), a.end());
int res = 1;
for (int i = 0; i < a.back(); ++i) {
int wh;
for (int j = 0; ; ++j) if (a[j] > i) {
wh = j; break;
}
if (a[wh] - i - 1 > i) continue;
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
f[wh][a[wh] - i - 1] = 1;
for (int j = wh; j < (int)a.size(); ++j) {
for (int k = 500; ~k; --k) {
((g[j][k] += g[j][k + 1]) >= P) && (g[j][k] -= P);
((f[j][k] += g[j][k]) >= P) && (f[j][k] -= P);
}
if (j == (int)a.size() - 1) break;
int D = a[j + 1] - a[j];
for (int k = 0; k <= 500; ++k) if (f[j][k]) {
if (k + D <= i) ((f[j + 1][k + D] += f[j][k]) >= P) && (f[j + 1][k + D] -= P);
((g[j + 1][min(D - 1, i)] += f[j][k]) >= P) && (g[j + 1][min(D - 1, i)] -= P);
}
}
for (int j = 0; j <= 500; ++j) ((res += f[a.size() - 1][j]) >= P) && (res -= P);
}
printf("%d\n", res);
return 0;
}
题目大意
给定长度为
的数组 ,你需要对于每个 求出,如果将 ,有多少种将 划分为若干个子串的方式满足每一个子串的长度均至少为其中元素的最大值,对 取模。
数据范围:, 。
思路分析
如果没有这个修改,只要求答案的话,可以使用 cdq 分治简单解决,对于跨过中点的询问枚举最大值在左侧还是右侧,做个二维偏序即可。
这样还可以顺便求出
对于每个
考虑仍然在 cdq 分治的基础上动手脚,我们枚举
对于每层内的
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, P = 998244353;
int n, a[N], f[N], g[N], h[N], res[N], s[N], suf[N];
vector<int> p[N];
void modify(int x, int y) {
for (; x <= n; x += x & -x) ((s[x] += y) >= P) && (s[x] -= P);
}
int query(int x) {
int S = 0;
for (; x; x -= x & -x) ((S += s[x]) >= P) && (S -= P);
return S;
}
void dfs1(int l, int r) {
if (l > r) return;
if (l == r) {
if (a[l] == 1) ((f[l] += f[l - 1]) >= P) && (f[l] -= P);
return;
}
int mid = (l + r) >> 1;
dfs1(l, mid);
for (int i = mid + 1; i <= r; ++i) p[i].clear();
for (int i = mid, o = a[i]; i >= l; --i) {
o = max(o, a[i]);
if (i + o - 1 <= r) p[max(i + o - 1, mid + 1)].push_back(i);
}
for (int i = mid + 1, o = a[i]; i <= r; ++i) {
for (auto v : p[i]) if (f[v - 1]) modify(v, f[v - 1]);
o = max(o, a[i]);
if (i - o + 1 >= l) ((f[i] += query(min(i - o + 1, mid))) >= P) && (f[i] -= P);
}
for (int i = mid + 1; i <= r; ++i) {
for (auto v : p[i]) if (f[v - 1]) modify(v, P - f[v - 1]);
}
dfs1(mid + 1, r);
}
void dfs2(int l, int r, int d) {
if (l >= r) return;
int mid = (!d ? (l + r) >> 1 : n - (((n - l + 1) + (n - r + 1)) >> 1));
dfs2(l, mid, d), dfs2(mid + 1, r, d);
for (int i = mid + 1; i <= r; ++i) p[i].clear();
for (int i = mid, o = a[i]; i >= l; --i) {
o = max(o, a[i]);
if (i + o - 1 <= r) p[max(i + o - 1, mid + 1)].push_back(i);
}
for (int i = mid + 1, o = -1, wh = 0, o2 = -1; i <= r; ++i) {
for (auto v : p[i]) if (f[v - 1]) modify(v, f[v - 1]);
if (a[i] > o) o2 = o, o = a[i], wh = i;
else o2 = max(o2, a[i]);
int tf = 0;
if (i - o + 1 >= l) {
int V = 1ll * (tf = query(min(i - o + 1, mid))) * g[i + 1] % P;
suf[i] = V;
} else {
tf = suf[i] = 0;
}
if (i - o2 + 1 >= l) {
h[wh] = (h[wh] + 1ll * (query(min(i - o2 + 1, mid)) - tf + P) * g[i + 1]) % P;
}
}
for (int i = r; i >= mid + 1; --i) {
if (i != r) ((suf[i] += suf[i + 1]) >= P) && (suf[i] -= P);
((h[i] += suf[i]) >= P) && (h[i] -= P);
}
for (int i = mid + 1; i <= r; ++i) {
for (auto v : p[i]) if (f[v - 1]) modify(v, P - f[v - 1]);
}
}
signed main() {
freopen("divide.in", "r", stdin);
freopen("divide.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
reverse(a + 1, a + n + 1), f[0] = 1, dfs1(1, n), reverse(f, f + n + 2), memcpy(g, f, sizeof(g)), memset(f, 0, sizeof(f)), reverse(a + 1, a + n + 1), f[0] = 1, dfs1(1, n);
for (int i = 1; i <= n; ++i) h[i] = 1ll * f[i - 1] * g[i + 1] % P;
dfs2(1, n, 0), memcpy(res, h, sizeof(res)), reverse(f, f + n + 2), reverse(g, g + n + 2), swap(f, g), reverse(a + 1, a + n + 1), memset(h, 0, sizeof(h)), dfs2(1, n, 1), reverse(h + 1, h + n + 1);
for (int i = 1; i <= n; ++i) (((res[i] += h[i]) >= P) && (res[i] -= P)), printf("%d%c", res[i], " \n"[i == n]);
return 0;
}
题目大意
给定
个三元有序数对 ,求它们任意排列后所需的最少操作次数使得 均不降,其中一次操作为选择一个元素加 。
数据范围:, 。
思路分析
考虑改为最小化
将每个点视作三维坐标画在空间直角坐标系上,那么合法当且仅当存在一条每次只向右前上的折线经过每一个点。
如果这条折线确定了,那么每个
时间复杂度为
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 255;
int a[N], b[N], c[N];
long long f[N][N][N];
int sa[N][N][N], sb[N][N][N], sc[N][N][N];
signed main() {
freopen("d.in", "r", stdin);
freopen("d.out", "w", stdout);
int n; scanf("%d", &n);
vector<int> la, lb, lc;
la.push_back(0), lb.push_back(0), lc.push_back(0);
long long sum = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &a[i], &b[i], &c[i]);
la.push_back(a[i]);
lb.push_back(b[i]);
lc.push_back(c[i]);
sum += (long long)a[i] + b[i] + c[i];
}
sort(la.begin(), la.end());
sort(lb.begin(), lb.end());
sort(lc.begin(), lc.end());
la.erase(unique(la.begin(), la.end()), la.end());
lb.erase(unique(lb.begin(), lb.end()), lb.end());
lc.erase(unique(lc.begin(), lc.end()), lc.end());
for (int i = 1; i <= n; ++i) {
int ta = lower_bound(la.begin(), la.end(), a[i]) - la.begin();
int tb = lower_bound(lb.begin(), lb.end(), b[i]) - lb.begin();
int tc = lower_bound(lc.begin(), lc.end(), c[i]) - lc.begin();
++sa[ta][tb][tc], ++sb[ta][tb][tc], ++sc[ta][tb][tc];
}
for (int i = 0; i < (int)la.size(); ++i) {
for (int j = 0; j < (int)lb.size(); ++j) {
for (int k = 0; k < (int)lc.size(); ++k) {
sa[i][j][k] += (j ? sa[i][j - 1][k] : 0) + (k ? sa[i][j][k - 1] : 0) - (j && k ? sa[i][j - 1][k - 1] : 0);
sb[i][j][k] += (i ? sb[i - 1][j][k] : 0) + (k ? sb[i][j][k - 1] : 0) - (i && k ? sb[i - 1][j][k - 1] : 0);
sc[i][j][k] += (i ? sc[i - 1][j][k] : 0) + (j ? sc[i][j - 1][k] : 0) - (i && j ? sc[i - 1][j - 1][k] : 0);
}
}
}
memset(f, 0x3f, sizeof(f));
f[0][0][0] = 0;
for (int i = 0; i < (int)la.size(); ++i) {
for (int j = 0; j < (int)lb.size(); ++j) {
for (int k = 0; k < (int)lc.size(); ++k) {
if (i + 1 != la.size()) {
f[i + 1][j][k] = min(f[i + 1][j][k], f[i][j][k] + ((long long)la[i + 1] + lb[j] + lc[k]) * sa[i + 1][j][k]);
}
if (j + 1 != lb.size()) {
f[i][j + 1][k] = min(f[i][j + 1][k], f[i][j][k] + ((long long)la[i] + lb[j + 1] + lc[k]) * sb[i][j + 1][k]);
}
if (k + 1 != lc.size()) {
f[i][j][k + 1] = min(f[i][j][k + 1], f[i][j][k] + ((long long)la[i] + lb[j] + lc[k + 1]) * sc[i][j][k + 1]);
}
}
}
}
printf("%lld\n", f[la.size() - 1][lb.size() - 1][lc.size() - 1] - sum);
return 0;
}
电力公司
题目大意
在直线
和 的所有 上都有一座房子( )或电塔 ,激活 上的房子需要 代价,激活 上的电塔需要 代价,你可以在激活的第 座房子和第 座电塔连线可以获得 的收益,要求连线仅在建筑物处相交, 次询问,每次给定两个子区间 ,询问在只考虑 内的房子和 内的电塔时的最大收益。
数据范围:, 。
思路分析
考虑全局询问怎么做,我们可以使用动态规划来解决,一种合理的状态设计是:
这里的未钦定可以在后续操作中被选到,没有房子水塔均被钦定选择的状态的原因是均可以被表示为
在转移中,为了增加转移路径数量,我们不妨假设一次只进行一步操作(移动上端点,移动下端点,激活房子,激活电塔)或扰动尽量小,这样可以得到
考虑多组询问,这样,问题就转化为了 DAG 上求
此时“宽松”地设计状态的好处就出来了:如果选择了至少一座建筑,那么对于任意
所以,我们使用分治的方式,每次选取区间长度较大的那一维(有四个参数
例如,假如
故总时间复杂度为
代码呈现
#include <bits/stdc++.h>
using namespace std;
const int N = 305, Q = 1e5 + 5;
int ox[N], oy[N], ow[N][N], res[Q], f[N][N], g[N][N], h[N][N], tf[N][N], tg[N][N], th[N][N];
void solve(int l1, int r1, int l2, int r2, vector< pair< pair< pair<int, int>, pair<int, int> >, int> > qr) {
if (qr.empty()) return;
if (r1 - l1 > r2 - l2) {
int mid = (l1 + r1) >> 1;
vector< pair< pair< pair<int, int>, pair<int, int> >, int> > qr1, qr2, tqr;
for (auto [x, y] : qr) {
if (x.first.second < mid) qr1.push_back({x, y});
else if (x.first.first > mid) qr2.push_back({x, y});
else tqr.push_back({x, y});
}
solve(l1, mid - 1, l2, r2, qr1), solve(mid + 1, r1, l2, r2, qr2);
if (tqr.empty()) return;
for (int o = l2; o <= r2; ++o) {
for (int i = l1 - 1; i <= r1 + 1; ++i) {
for (int j = l2 - 1; j <= r2 + 1; ++j) {
f[i][j] = g[i][j] = h[i][j] = -1e9;
tf[i][j] = tg[i][j] = th[i][j] = -1e9;
}
}
h[mid][o] = th[mid][o] = 0;
for (int i = mid; i <= r1; ++i) {
for (int j = o; j <= r2; ++j) {
f[i][j] = max({f[i][j], f[i - 1][j], f[i][j - 1], g[i - 1][j], h[i][j - 1]});
g[i][j] = max({g[i][j], f[i][j] - ox[i], g[i][j - 1], g[i][j - 1] + ow[i][j - 1] - oy[j - 1], h[i][j - 1] + ow[i][j - 1] - ox[i]});
h[i][j] = max({h[i][j], f[i][j] - oy[j], h[i - 1][j], h[i - 1][j] + ow[i - 1][j] - ox[i - 1], g[i - 1][j] + ow[i - 1][j] - oy[j]});
}
}
for (int i = mid; i >= l1; --i) {
for (int j = o; j >= l2; --j) {
tf[i][j] = max({tf[i][j], tg[i][j] - ox[i], th[i][j] - oy[j]});
tf[i - 1][j] = max(tf[i - 1][j], tf[i][j]), tf[i][j - 1] = max(tf[i][j - 1], tf[i][j]);
tg[i - 1][j] = max(tg[i - 1][j], tf[i][j]), th[i][j - 1] = max(th[i][j - 1], tf[i][j]);
tg[i][j - 1] = max({tg[i][j - 1], tg[i][j], tg[i][j] + ow[i][j - 1] - oy[j - 1]});
tg[i - 1][j] = max(tg[i - 1][j], th[i][j] + ow[i - 1][j] - oy[j]);
th[i - 1][j] = max({th[i - 1][j], th[i][j], th[i][j] + ow[i - 1][j] - ox[i - 1]});
th[i][j - 1] = max(th[i][j - 1], tg[i][j] + ow[i][j - 1] - ox[i]);
}
}
for (auto [x, y] : tqr) {
if (x.second.first <= o && o <= x.second.second) {
res[y] = max(res[y], tf[x.first.first][x.second.first] + f[x.first.second][x.second.second]);
}
}
}
} else {
int mid = (l2 + r2) >> 1;
vector< pair< pair< pair<int, int>, pair<int, int> >, int> > qr1, qr2, tqr;
for (auto [x, y] : qr) {
if (x.second.second < mid) qr1.push_back({x, y});
else if (x.second.first > mid) qr2.push_back({x, y});
else tqr.push_back({x, y});
}
solve(l1, r1, l2, mid - 1, qr1), solve(l1, r1, mid + 1, r2, qr2);
if (tqr.empty()) return;
for (int o = l1; o <= r1; ++o) {
for (int i = l1 - 1; i <= r1 + 1; ++i) {
for (int j = l2 - 1; j <= r2 + 1; ++j) {
f[i][j] = g[i][j] = h[i][j] = -1e9;
tf[i][j] = tg[i][j] = th[i][j] = -1e9;
}
}
g[o][mid] = tg[o][mid] = 0;
for (int i = o; i <= r1; ++i) {
for (int j = mid; j <= r2; ++j) {
f[i][j] = max({f[i][j], f[i - 1][j], f[i][j - 1], g[i - 1][j], h[i][j - 1]});
g[i][j] = max({g[i][j], f[i][j] - ox[i], g[i][j - 1], g[i][j - 1] + ow[i][j - 1] - oy[j - 1], h[i][j - 1] + ow[i][j - 1] - ox[i]});
h[i][j] = max({h[i][j], f[i][j] - oy[j], h[i - 1][j], h[i - 1][j] + ow[i - 1][j] - ox[i - 1], g[i - 1][j] + ow[i - 1][j] - oy[j]});
}
}
for (int i = o; i >= l1; --i) {
for (int j = mid; j >= l2; --j) {
tf[i][j] = max({tf[i][j], tg[i][j] - ox[i], th[i][j] - oy[j]});
tf[i - 1][j] = max(tf[i - 1][j], tf[i][j]), tf[i][j - 1] = max(tf[i][j - 1], tf[i][j]);
tg[i - 1][j] = max(tg[i - 1][j], tf[i][j]), th[i][j - 1] = max(th[i][j - 1], tf[i][j]);
tg[i][j - 1] = max({tg[i][j - 1], tg[i][j], tg[i][j] + ow[i][j - 1] - oy[j - 1]});
tg[i - 1][j] = max(tg[i - 1][j], th[i][j] + ow[i - 1][j] - oy[j]);
th[i - 1][j] = max({th[i - 1][j], th[i][j], th[i][j] + ow[i - 1][j] - ox[i - 1]});
th[i][j - 1] = max(th[i][j - 1], tg[i][j] + ow[i][j - 1] - ox[i]);
}
}
for (auto [x, y] : tqr) {
if (x.first.first <= o && o <= x.first.second) {
res[y] = max(res[y], tf[x.first.first][x.second.first] + f[x.first.second][x.second.second]);
}
}
}
}
}
signed main() {
freopen("tower.in", "r", stdin);
freopen("tower.out", "w", stdout);
int n, q; scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &ox[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &oy[i]);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) scanf("%d", &ow[i][j]);
}
vector< pair< pair< pair<int, int>, pair<int, int> >, int> > qr;
for (int i = 1; i <= q; ++i) {
int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d);
qr.push_back({{{a, b + 1}, {c, d + 1}}, i});
}
solve(1, n + 1, 1, n + 1, qr);
for (int i = 1; i <= q; ++i) {
printf("%d\n", res[i]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!