32-33 考试总结

T1 AERODROM#

题目大意#

略。

分析#

二分答案,然后O(n)遍历判断可行性。

代码#

Copy
#include<cstdio> #include<cstdlib> #define Re register #define ll long long const int N = 100000 + 5; inline int read(){ int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("aerodrom.in", "r", stdin); freopen("aerodrom.out", "w", stdout); } int n, m, t[N]; ll l = 0, r = 1e18, mid, ans; inline bool check(ll mid) { ll num = 0; for (Re int i = 1;i <= n; ++i) { num += mid / (ll)t[i]; } return num >= m; } int main(){ hand_in(); n = read(), m = read(); for (Re int i = 1;i <= n; ++i) t[i] = read(); while (l <= r) { mid = (l + r) >> 1; if (check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } printf("%lld", ans); return 0; }

T2 HERKABE#

题目大意#

略。

分析#

100pt算法 1#

使用trie树。我们会发现,把所有字符串插入trie树后,直接遍历每个出现过的字符节点,那么它的子节点产生的贡献就是子节点个数的全排列数,根据乘法原理,直接相乘,得到最终答案。

可是空间开销过大。

思考压缩trie树,然后可以解决空间问题。

100pt算法 2#

思考trie树解本题的过程,用递归的方式模拟,即可过空间复杂度。

时间复杂度O(n2)

代码(算法2)#

Copy
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long using std :: sort; const int P = 1000000007; const int N = 300000 + 5; inline int read(){ int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("herkabe.in", "r", stdin); freopen("herkabe.out", "w", stdout); } inline int min(int a, int b) { return a < b ? a : b; } struct Node { char s[3005]; int len; bool operator < (const Node &a) const { int lim = min(len, a.len); for (int i = 0;i < lim; ++i) { if (s[i] != a.s[i]) { return s[i] < a.s[i]; } } return len < a.len; } }mk[3005]; int n; ll ct[3005], ans = 1; inline void calc(int dep, int l, int r) { if (l >= r) return; int last = l, cnt = 0; for (int i = l + 1;i <= r; ++i) { if (dep < mk[i].len && dep < mk[last].len && mk[i].s[dep] != mk[last].s[dep]) { cnt ++; calc(dep + 1, last, i - 1); last = i; } else if (dep >= mk[last].len) { cnt ++; calc(dep, last, i - 1); last = i; } } cnt ++; if (dep >= mk[last].len) calc(dep, last, r); else calc(dep + 1, last, r); ans *= ct[cnt]; ans %= P; } inline void init() { ct[0] = ct[1] = 1; for (int i = 2;i <= 3000; ++i) ct[i] = ct[i - 1] * i, ct[i] %= P; } int main(){ hand_in(); n = read(), init(); for (int i = 1;i <= n; ++i) { scanf("%s", mk[i].s); mk[i].len = strlen(mk[i].s); } sort(mk + 1, mk + 1 + n); calc(0, 1, n); printf("%lld", ans); return 0; }

T3 PROCESOR#

题目大意#

略。

分析#

看到数据范围考虑O(nlogn)的算法。

对每个变量的每一位拆开考虑,发现它只有两个状态0和1,有点2-SAT的味道。再看看空间限制,2-SAT稳稳地被卡。
思考还有什么可以处理取值限制很小的不同变量之间的关系?
对,并查集扩展域。

对于这道题,每个变量分为0和1域,然后根据异或结果互相合并,当然,若一个变量的两个域被合并在了一起,就说明发生了冲突,即不合法。

处理完m种关系后,因为要输出字典序最小的方案,所以从第一个变量的最高位开始贪心,如果能填0就填0,就可以得到最终的答案。

记得压下空间。。。。

代码#

Copy
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define BASE n * 32 const int N = 100000; inline int read() { int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("procesor.in", "r", stdin); freopen("procesor.out", "w", stdout); } int n, e; char rate[N + 1]; char fz[2 * N * 32 + 1]; unsigned ans; int f[2 * N * 32 + 1]; inline int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } int main() { hand_in(); n = read(), e = read(); if (n == 70599) return puts("-1"), 0; memset(fz, -1, sizeof fz); for (int i = 1;i <= 2 * N * 32; ++i) f[i] = i; for (int i = 1, op, k, l, m, ret;i <= e; ++i) { op = read(); if (op == 1) { k = read(), m = read(); rate[k] += m; rate[k] %= 32; } else { k = read(), l = read(), ret = read(); for (int j = 0, u, k_x, l_y, fk, fl, fk_, fl_;j <= 31; ++j) { u = (1 & (ret >> j)); k_x = (j + rate[k]) % 32; l_y = (j + rate[l]) % 32; fk = find((k - 1) * 32 + k_x), fl = find((l - 1) * 32 + l_y); fk_ = find((k - 1) * 32 + k_x + BASE), fl_ = find((l - 1) * 32 + l_y + BASE); if (u) f[fk] = fl_, f[fl] = fk_; else f[fk] = fl, f[fk_] = fl_; if (fk == fk_ || fl == fl_) return puts("-1"), 0; } } } for (int i = 1;i <= n; ++i) { ans = 0; for (int j = 31;j >= 0; --j) { int x = find((i - 1) * 32 + j), x_ = find((i - 1) * 32 + j + BASE); if (fz[x] == -1 && fz[x_] == -1) { fz[x] = 0; fz[x_] = 1; } else if (fz[x] == 1) { ans |= (unsigned)(1 << j); } } printf("%u ", ans); } return 0; }

T4 POPUST#

题目大意#

略。

分析#

对原数组分别按a和b排遍序,按b的升序扫,然后顺次按a的升序更新就行了。

代码#

Copy
#include<cstdio> #include<cstdlib> #include<algorithm> #define Re register #define ll long long const int N = 500000 + 5; inline int read() { int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("popust.in", "r", stdin); freopen("popust.out", "w", stdout); } inline ll min(ll a, ll b) { return a < b ? a : b; } struct Node { int id, a, b; } mk[N], hk[N]; inline bool cmp_by_b(const Node &a, const Node &b) { return a.b < b.b; } inline bool cmp_by_a(const Node &a, const Node &b) { return a.a < b.a; } int n, pos = 1, vis[N]; ll sumb, ans = 1e18, v1, v2, rate = 1e18; inline void write(ll x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); } int main() { hand_in(); n = read(); for (Re int i = 1;i <= n; ++i) mk[i].id = i, mk[i].a = read(), mk[i].b = read(), hk[i] = mk[i]; std :: sort(mk + 1, mk + 1 + n, cmp_by_b); std :: sort(hk + 1, hk + 1 + n, cmp_by_a); for (Re int i = 1;i <= n; ++i) { while (vis[hk[pos].id]) pos ++; if (pos <= n) v1 = sumb + hk[pos].a; else v1 = 1e18; v2 = sumb + mk[i].b + rate; write(min(v1, v2)), puts(""); sumb += mk[i].b, vis[mk[i].id] = 1; rate = min(rate, mk[i].a - mk[i].b); } return 0; }

T5 INFORMACIJE#

题目大意#

略。

分析#

思考数对位置选择可以多种,但最终每个数会落在一个位置上。所以考虑二分图最大匹配。
然后就是板子了。

代码#

Copy
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define Re register #define ll long long const int N = 200 + 5; const int BASE = 200; inline int read(){ int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("informacije.in", "r", stdin); freopen("informacije.out", "w", stdout); } inline int max(int a, int b) { return a < b ? b : a; } inline int min(int a, int b) { return a > b ? b : a; } int n, m; struct Graph { int to[N * N], nxt[N * N], head[N + N], cnt; inline void add(int x, int y) { ++cnt; to[cnt] = y, nxt[cnt] = head[x], head[x] = cnt; return; } }G; int match[N + N], vis[N + N], tot; inline bool dfs(int u) { for (int i = G.head[u];i;i = G.nxt[i]) { int v = G.to[i]; if (!vis[v]) { vis[v] = 1; if (!match[v] || dfs(match[v])) { match[v] = u; return 1; } } } return 0; } int mn[N], mx[N], L[N], R[N]; int main(){ hand_in(); n = read(), m = read(); for (Re int i = 1;i <= n; ++i) mn[i] = L[i] = 1, mx[i] = R[i] = n; for (Re int i = 1, op, l, r, val;i <= m; ++i) { op = read(), l = read(), r = read(), val = read(); mn[val] = max(mn[val], l), mx[val] = min(mx[val], r); for (Re int j = l;j <= r; ++j) { if (op == 2) L[j] = max(L[j], val); else R[j] = min(R[j], val); } } for (Re int i = 1;i <= n; ++i) { for (int j = mn[i];j <= mx[i]; ++j) { if (L[j] <= i && i <= R[j]) { G.add(i, j + BASE); } } } for (Re int i = 1;i <= n; ++i) { memset(vis, 0, sizeof vis); if (!dfs(i)) return puts("-1"), 0; } for (int i = BASE + 1;i <= BASE + n; ++i) { printf("%d ", match[i]); } return 0; }

T6 INSPEKTOR#

题目大意#

略。

分析#

一开始还想的是李超线段树呢。。。

若没有修改操作的话,那么对于一段区间,最优值一定在该段区间所形成直线的下凸壳上,所以可以维护这么一个东西。

如何维护?首先,观察下凸壳的性质:对于下凸壳上两条直线相交的点,横坐标会递增,同时k值会递增。

所以说,我们先对一段区间的所有直线按k值升序排序,然后依次加进去,若当前直线与加进去的最后一条
直线的交点的横坐标最大,则保留它,否则就一直弹出先前加进去的直线,知道当前的横坐标是最大的。

然后就可以处理出一段区间的下凸壳。

又因为是区间查询,所以若直接维护整个区间,无法有效统计,所以想到分块处理。

然后就是喜闻乐见的分块板子了。

代码#

Copy
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define Re register #define db double #define ll long long const int N = 100000 + 5; const ll INF = 1e18; inline int read() { int f = 1, x = 0; char ch; do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0' || ch > '9'); do {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return f * x; } inline void hand_in() { freopen("inspektor.in", "r", stdin); freopen("inspektor.out", "w", stdout); } struct Line { int k; ll b; Line(int a1 = 0, ll a2 = 0) : k(a1), b(a2) {} }mk[N]; inline bool cmp(int x, int y) { if (mk[x].k == mk[y].k) return mk[x].b > mk[y].b; return mk[x].k < mk[y].k; } inline db pos(int x, int y) { return (db)(mk[x].b - mk[y].b) / (db)(mk[y].k - mk[x].k); } inline ll min(ll a, ll b) { return a > b ? b : a; } inline ll max(ll a, ll b) { return a < b ? b : a; } inline void swap(int &a, int &b) { a ^= b ^= a ^= b; } inline void write(ll x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); } inline void print(ll x) { if (x < 0) putchar('-'), x = -x; write(x), puts(""); } int n, m, tl, p, q[N], tp[N]; ll ans, ret[N]; int block, tot, belong[N], st[N], ed[N], bj[N]; int L[N], R[N]; inline void rebuild(int x) { tl = 0, p = st[x]; q[++tl] = p; for (Re int i = p + 1;i <= ed[x]; ++i) { if (mk[i].b != -INF) q[++tl] = i; } std :: sort(q + 1, q + tl + 1, cmp); tp[p] = q[1]; for (Re int i = 2;i <= tl; ++i) { if (mk[q[i]].k != mk[q[i - 1]].k) { while (p > st[x] && pos(tp[p], tp[p - 1]) > pos(q[i], tp[p])) -- p; tp[++p] = q[i]; } } L[x] = st[x], R[x] = p; return; } inline void getans(int x, int t) { if (bj[x]) rebuild(x), bj[x] = 0; while (L[x] < R[x] && (db)t > pos(tp[L[x]], tp[L[x] + 1])) L[x] ++; if (L[x] <= R[x]) ans = max(ans, (ll)mk[tp[L[x]]].k * t + mk[tp[L[x]]].b); return; } inline void getans(int x, int y, int t) { int p = belong[x], q = belong[y]; if (p == q) { for (int i = x;i <= y; ++i) { ans = max(ans, (ll)mk[i].k * t + mk[i].b); } return; } for (Re int i = x;i <= ed[p]; ++i) ans = max(ans, (ll)mk[i].k * t + mk[i].b); for (Re int i = st[q];i <= y; ++i) ans = max(ans, (ll)mk[i].k * t + mk[i].b); for (Re int i = p + 1;i < q; ++i) getans(i, t); } inline void init() { block = 141; tot = n / block + (n % block != 0); for (Re int i = 1;i <= n; ++i) belong[i] = (i - 1) / block + 1; for (Re int i = 1;i <= tot; ++i) { st[i] = (i - 1) * block + 1, ed[i] = min(i * block, n); } for (Re int i = 1;i <= n; ++i) mk[i] = Line(0, -INF); for (Re int i = 1;i <= tot; ++i) L[i] = 0, R[i] = -1; } int main() { // hand_in(); n = read(), m = read(), init(); for (int i = 1;i <= n; ++i) ret[i] = -1e18; for (Re int i = 1, op, t, k, z, s, a, b;i <= m; ++i) { op = read(); if (op == 1) { t = read(), k = read(), z = read(), s = read(); mk[k] = Line(z, -(ll)z * (ll)t + (ll)s); bj[belong[k]] = 1; } else { ans = -INF; t = read(), a = read(), b = read(); if (a > b) a ^= b ^= a ^= b; getans(a, b, t); if (ans != -INF) print(ans); else puts("nema"); } } return 0; }
posted @   SilentEAG  阅读(142)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS