2023.8.19 考试
每次考试都考得贼拉胯,2023.8.23 考得更烂,直接倒数,每次都没那红烧肉高,我也是醉了。
update 2023/8/24:从明天开始,加训。
考得稀烂。还没小学生考得好。膜拜 ltx,lwz!
T1: 可持久化变量
\rm 10pts
直接暴力。
\rm 100pts
维护变量的历史版本,每次操作后用数组记录当前变量的值,回溯状态时直接调用数组。复杂度
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, a[N], now;
signed main() {
// freopen("persistent.in", "r", stdin);
// freopen("persistent.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
string s;
int x;
cin >> s >> x;
if (s == "ADD") {
now += x;
a[i] = now;
}
if (s == "SUB") {
now -= x;
a[i] = now;
}
if (s == "SET") {
now = x;
a[i] = now;
}
if (s == "BACK") {
now = a[i - x - 1];
a[i] = now;
}
cout << now << ' ';
}
}
T2: 填数游戏
\rm 40pts
根据排序不等式,可以想到直接排序来选的思路。但其实是错的,因为值域会有负数情况。
\rm 100pts
所以我们把负数放一块,正数放一块。最负的数和最负的数相乘,最正的数和最正的数相乘。
当然还剩下一些数,一个数组中必然全是非负数,另一个数组中必然全是非正数。这个时候就可以用排序不等式了。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5;
int n, m, a[N], b[N];
int a1[N], a2[N], a3[N], b1[N], b2[N], b3[N], a1l, a2l, b1l, b2l, a3l, b3l;
int res;
map<int, int> vis1, vis2;
int ww[N], ww2[N], wl, wwl;
signed main() {
// freopen("fill.in", "r", stdin);
// freopen("fill.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i], vis1[a[i]]++;
for (int i = 1; i <= m; i++) cin >> b[i], vis2[b[i]]++;
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
for (int i = 1; i <= n; i++) {
if (a[i] > 0) a1[++a1l] = a[i];
if (a[i] < 0) a2[++a2l] = a[i];
if (a[i] == 0) a3[++a3l] = a[i];
}
for (int i = 1; i <= m; i++) {
if (b[i] > 0) b1[++b1l] = b[i];
if (b[i] < 0) b2[++b2l] = b[i];
if (b[i] == 0) b3[++b3l] = b[i];
}
if (a1l > b1l) {
for (int i = 1; i <= b1l; i++) {
res += a1[a1l - (b1l - i + 1) + 1] * b1[i];
vis1[a1[a1l - (b1l - i + 1) + 1]]--;
vis2[b1[i]]--;
}
}
if (a1l <= b1l) {
for (int i = 1; i <= a1l; i++) {
res += a1[i] * b1[b1l - (a1l - i + 1) + 1];
vis1[a1[i]]--;vis2[b1[b1l - (a1l - i + 1) + 1]]--;
}
}
if (a2l > b2l) {
for (int i = 1; i <= b2l; i++) {
res += a2[i] * b2[i];
vis1[a2[i]]--,vis2[b2[i]]--;
}
}
if (a2l <= b2l) {
for (int i = 1; i <= a2l; i++) {
res += a2[i] * b2[i];
vis1[a2[i]]--, vis2[b2[i]]--;
}
}
for (auto i = vis1.begin(); i != vis1.end(); i++) {
int t = i -> second;
while (t) {
ww[++wl]= i->first;
t--;
}
}
for (auto i = vis2.begin(); i != vis2.end(); i++) {
int t = i -> second;
while (t) {
ww2[++wwl]= i->first;
t--;
}
}
for (int i = 1; i <= wl; i++) {
res += ww[i] * ww2[wwl-(wl-i+1)+1];
}
cout << res << endl;
}
T3: 数数师
\rm 20pts
暴力 dfs,甚至你算最大子段和都可以三方来算。
\rm 50pts
可以计算答案
使用动态规划解决,设
\rm 100pts
容易发现这个转移可以使用前缀和优化将复杂度优化成
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, mod = 998244353;
int n, m, k, dp1[N], dp2[N];
int get(int x) {
memset(dp1, 0, sizeof dp1);
dp1[0] = 1;
int t = max(0, x);
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= t; j++) dp2[j] = 0;
for (int j = 1; j <= t; j++) dp1[j] = (dp1[j] + dp1[j - 1]) % mod;
for (int j = -m; j <= x; j++) {
if (j > m) dp2[max(j, 0)] = (dp2[max(j, 0)] + dp1[min(j + m, t)] - dp1[j - m - 1] + mod) % mod;
else dp2[max(j, 0)] = (dp2[max(j, 0)] + dp1[min(j + m, t)] ) % mod;
}
for (int j = 0; j <= t; j++) dp1[j] = dp2[j];
}
int res = 0;
for (int i = 0; i <= 2000; i++) res = (res + dp1[i]) % mod;
return res;
}
int main() {
cin >> n >> m >> k;
cout << (get(k) - get(k - 1) + mod) % mod << endl;
}
T4:Modulus
\rm 50pts
范围
\rm 100pts
考虑他取模很有规律的样子?
于是我们打出
找规律会发现这
找规律会发现区间范围为
然后就线段树加二分乱维护就行。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
int n, m, l[N], r[N], maxn[N], len;
struct edge {
int l, r, Max;
}tree[N * 4];
void push_up(int p) {
tree[p].Max = max(tree[p * 2].Max, tree[p *2 + 1].Max);
}
void build(int p, int l, int r) {
tree[p].l = l, tree[p].r = r;
if (l == r) {
tree[p].Max = maxn[l];
return;
}
int mid = (tree[p].l + tree[p].r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
push_up(p);
}
int query(int p, int l, int r) {
if (l <= tree[p].l && tree[p].r <= r) return tree[p].Max;
int res = 0, mid = (tree[p].l + tree[p].r) >> 1;
if (l <= mid) res = max(res, query(p * 2, l, r));
if (r > mid) res = max(res, query(p * 2 + 1, l, r));
return res;
}
signed main() {
cin >> n >> m;
int id =0 ;
for (int i = 1; i <= n; i++) {
int t1 = n / (i + 1) + 1, t2 = n / i;
if (t1 > t2) break;
l[++len] = t1, r[len] = t2;
// cout << min(t1, t2) << ' ' << t2 << endl;
}
for (int i = l[len] - 1; i >= 1; i--) l[++len] = i, r[len] = i;
for (int i = 1; i <= len; i++) maxn[i] = n % r[i] + (r[i] - l[i]) * i;
reverse(l + 1, l + len + 1);
reverse(r + 1, r + len + 1);
reverse(maxn + 1, maxn + len + 1);
build(1, 1, len);
while (m--) {
int L, R;
cin >> L >> R;
int LL = 1, RR = len;
while (LL < RR) {
int mid = (LL + RR + 1) >> 1;
if (l[mid] <= L) LL = mid;
else RR = mid - 1;
}
int t1 = LL;
LL = 1, RR = len;
while (LL < RR) {
int mid = (LL + RR + 1) >> 1;
if (l[mid] <= R) LL = mid;
else RR = mid - 1;
}
int t2 = LL;
// cout << t1 << ' ' << t2 << endl;
if (t1 == t2) cout << n % L << endl;
else if (t1 + 1 == t2) cout << max(n % L, n % l[t2]) << endl;
else cout << max(n % L, max(query(1, t1 + 1, t2 - 1), n % l[t2])) << endl;
}
}