2.10 L5-NOIP模拟11 测试题解
经典老题
A. [CQOI2009] 中位数
题意
给出
对于
思路
首先在输入的时候记录一下
然后发现要找的子串一定是包含
对于一个满足条件的子串,大于
在分别往左右找的过程中,设当前比
那么要找的子串一定满足:左边的
于是在左边时用数组
数组下标可能有负数,但绝对值不超过
肥肠的暴力。
代码
#include <cstdio>
#include <iostream>
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
#define g(x, y, z) for (int x = (y); x >= (z); --x)
#define FILENAME "median"
using namespace std;
int const N = 1e5 + 10;
int const BASE = 5e4;
int n, a[N], b, ans, pos, delta[N];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
// freopen(FILENAME".in", "r", stdin);
// freopen(FILENAME".out", "w", stdout);
cin >> n >> b;
f(t, 1, n) {
cin >> a[t];
if (a[t] == b) pos = t;
}
int cnt1 = 0, cnt2 = 0;
delta[BASE] = 1;
g(i, pos - 1, 1) {
if (a[i] < a[pos]) ++cnt1;
else ++cnt2;
++delta[cnt1 - cnt2 + BASE];
}
cnt1 = cnt2 = 0;
f(i, pos + 1, n) {
if (a[i] < a[pos]) ++cnt1;
else ++cnt2;
ans += delta[cnt2 - cnt1 + BASE];
}
cout << ans + delta[BASE] << '\n';
return 0;
}
B. [HNOI2004] 敲砖块(朴素 DP)
题意
在一个凹槽中放置了
如果你想敲掉第
你现在可以敲掉最多
对于
思路
设
根据题意,要想敲掉第
而第
现在我们想办法将第
枚举第
时间复杂度
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#define f(x, y, z) for (int x = (y); (x) <= (z); ++(x))
#define g(x, y, z) for (int x = (y); (x) >= (z); --(x))
#define FILENAME "brike"
using namespace std;
int const N = 55, M = 1255;
int const INF = 0x3f3f3f3f;
int n, m, a[N][N], f[N][N][M];
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
// freopen(FILENAME".in", "r", stdin);
// freopen(FILENAME".out", "w", stdout);
cin >> n >> m;
f(i, 1, n) f(j, 1, n - i + 1) cin >> a[i][j];
memset(f, 0xc0, sizeof f);
f[n + 1][0][0] = 0;
g(j, n, 1) {
int sum = 0;
f(i, 0, n - j + 1) {
sum += a[i][j];
f(k, i, m) {
int &tmp = f[j][i][k];
f(t, i ? i - 1 : 0, n - j)
tmp = max(tmp, f[j + 1][t][k - i] + sum);
}
}
}
int ans = -INF;
f(i, 1, n) f(j, 1, n - i + 1) ans = max(ans, f[i][j][m]);
cout << ans << '\n';
return 0;
}
C. 最小密度路径(魔改 Floyd)
题意
给出一张有
思路
首先发现
如果边数一定,那么问题就变成了纯纯的最短路问题。于是尝试枚举边数。
设
也就是说,我们在 Floyd 算法中的最短路上又附加了一维信息——边数。
枚举中间点
然而我们发现时间复杂度为
对于这样一条路径:
于是复杂度就神奇地优化成了
答案:
初值:对于不合法的状态(即
代码
#include <cstdio>
#include <iomanip>
#include <cstring>
#include <iostream>
#define f(x, y, z) for (int x = (y); (x) <= (z); ++(x))
#define FILENAME "path"
using namespace std;
typedef double db;
int const N = 55, M = 1010;
int const INF = 0x3f3f3f3f;
int n, m, q, d[N][N][M];
db ans;
inline void getmin(int &p, int const &q) { p = min(p, q); }
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
// freopen(FILENAME".in", "r", stdin);
// freopen(FILENAME".out", "w", stdout);
cin >> n >> m;
memset(d, 0x3f, sizeof d);
f(i, 1, m) {
int a, b, w;
cin >> a >> b >> w;
getmin(d[a][b][1], w);
}
f(l, 2, m) f(k, 1, n) f(i, 1, n) f(j, 1, n) {
int &t = d[i][j][l];
int val = d[i][k][1] + d[k][j][l - 1];
getmin(t, val);
}
cin >> q;
cout << fixed << setprecision(3);
while (q--) {
int x, y;
cin >> x >> y;
ans = INF;
f(l, 1, n - 1) {
if (d[x][y][l] == INF) continue;
ans = min(ans, d[x][y][l] * 1.0 / l);
}
if (ans == INF) cout << "OMG!\n";
else cout << ans << '\n';
}
return 0;
}
D. [SCOI2006]动态最值(线段树 + 树状数组 / 魔改线段树)
题意
有一个包含
-
DELETE k
:删除位置 上的数。右边的数往左移一个位置(即动态的数组下标)。 -
QUERY i j
:查询位置 上所有数的最小值和最大值。
对于
思路及代码
线段树 + 树状数组
(考试做法)
设删去一些数后的数组下标为
每删去一个
但是,题目中给出的是
时间复杂度
代码:
#include <cstdio>
#include <iostream>
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
#define FILENAME "minmax"
using namespace std;
typedef pair<int, int> pii;
int const N = 1e6 + 10;
int const INF = 0x3f3f3f3f;
int n, m, a[N];
struct BIT {
#define lowbit(x) (x & (-x))
... //省略
} del;
struct SegTree {
#define lson (u << 1)
#define rson (u << 1 | 1)
struct Node {
int l, r, minn, maxx;
} tr[N << 2];
inline void pushup(int u) {
tr[u].minn = min(tr[lson].minn, tr[rson].minn);
tr[u].maxx = max(tr[lson].maxx, tr[rson].maxx);
return;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].minn = tr[u].maxx = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(u);
return;
}
void change(int u, int x, int mn, int mx) {
if (tr[u].l == tr[u].r && tr[u].l == x) {
tr[u].minn = mn;
tr[u].maxx = mx;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) change(lson, x, mn, mx);
else change(rson, x, mn, mx);
pushup(u);
return;
}
inline void cmp(pii &p, pii q) {
p.first = min(p.first, q.first);
p.second = max(p.second, q.second);
return;
}
pii get_minmax(int u, int l, int r) {
pii res = { INF, -INF };
if (l <= tr[u].l && tr[u].r <= r)
return make_pair(tr[u].minn, tr[u].maxx);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) cmp(res, get_minmax(lson, l, r));
if (r > mid) cmp(res, get_minmax(rson, l, r));
return res;
}
} tr;
int val(int x) {
int l = 0, r = n, mid; //注意l的初值为0.因为r可能等于1
while (l + 1 < r) {
mid = (l + r) >> 1;
if (mid - del.sum(mid) < x) l = mid;
else r = mid;
}
return r;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
// freopen(FILENAME ".in", "r", stdin);
// freopen(FILENAME ".out", "w", stdout);
cin >> n >> m;
f(i, 1, n) cin >> a[i];
tr.build(1, 1, n);
f(i, 1, m) {
int op, x, y;
cin >> op;
if (op == 1) {
cin >> x;
x = val(x);
tr.change(1, x, INF, -INF);
del.add(x, 1);
} else if (op == 2) {
cin >> x >> y;
x = val(x), y = val(y);
pii ans = tr.get_minmax(1, x, y);
cout << ans.first << ' ' << ans.second << '\n';
}
}
return 0;
}
魔改线段树
考虑在线段树上直接记录当前区间被删除的情况。记录
于是我们在查询或删除的时候,直接传入 当前区间第几个没被删除的数。
注意下面的
删除
查询区间
,说明在左儿子,查询左儿子中的 ; ,说明在右儿子,查询右儿子中的 ;- 否则,说明横跨左儿子和右儿子,分别查询左儿子中的
和右儿子中的 。
时间复杂度
代码:
#include <cctype>
#include <cstdio>
#include <iostream>
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
#define FILENAME "minmax"
using namespace std;
typedef pair<int, int> pii;
int const N = 1e6 + 10;
int const INF = 0x3f3f3f3f;
int n, m, a[N];
struct SegTree {
#define lson (u << 1)
#define rson (u << 1 | 1)
struct Node {
int l, r, minn, maxx, sum;
} tr[N << 2];
inline void pushup(int u) {
tr[u].minn = min(tr[lson].minn, tr[rson].minn);
tr[u].maxx = max(tr[lson].maxx, tr[rson].maxx);
tr[u].sum = tr[lson].sum + tr[rson].sum;
return;
}
void build(int u, int l, int r) {
tr[u].l = l ,tr[u].r = r;
if (l == r) {
tr[u].minn = tr[u].maxx = a[l];
tr[u].sum = 1;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(u);
return;
}
void remove(int u, int x) {
if (tr[u].l == tr[u].r) {
tr[u].minn = INF, tr[u].maxx = -INF;
tr[u].sum = 0;
return;
}
int lsum = tr[lson].sum;
if (x <= lsum) remove(lson, x);
else remove(rson, x - lsum);
pushup(u);
return;
}
inline void cmp(pii &p, pii q) {
p.first = min(p.first, q.first);
p.second = max(p.second, q.second);
return;
}
pii get_minmax(int u, int l, int r) {
pii res = {INF, -INF};
if (l == 1 && r == tr[u].sum)
return make_pair(tr[u].minn, tr[u].maxx);
int lsum = tr[lson].sum, rsum = tr[rson].sum;
if (l <= lsum && r > lsum) cmp(res, get_minmax(lson, l, lsum)), cmp(res, get_minmax(rson, 1, r - lsum));
else if (l <= lsum && r <= lsum) cmp(res, get_minmax(lson, l, r));
else if (l > lsum && r <= lsum + rsum) cmp(res, get_minmax(rson, l - lsum, r - lsum));
return res;
}
} tr;
signed main() {
read(n, m); //快读快写模板省略
f(i, 1, n) read(a[i]);
tr.build(1, 1, n);
f(i, 1, m) {
int op, x, y;
read(op);
if (op == 1) {
read(x);
tr.remove(1, x);
} else if (op == 2) {
read(x, y);
pii t = tr.get_minmax(1, x, y);
print(t.first, t.second);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】