第29次CCF计算机软件能力认证
100+100+100+100+60=460
田地丈量
题目大意
二维平面,给定一个矩阵区域和若干个互不相交的矩形区域组 。
问 和 的相交面积和。
解题思路
因为中矩形互不相交,因此可以分别计算与 的面积交,相加即是答案,不会有重。
神奇的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, a, b; cin >> n >> a >> b; LL ans = 0; auto calc = [&](int x1, int y1, int x2, int y2){ int l = max(x1, 0); int r = max(y1, 0); int L = min(a, x2); int R = min(b, y2); return 1ll * max(L - l, 0) * max(R - r, 0); }; for(int i = 1; i <= n; ++ i){ int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; ans += calc(x1, y1, x2, y2); } cout << ans << '\n'; return 0; }
垦田计划
题目大意
给定一个个数的数组,对于每个 数,可花 的代价令其减一。但每个数最低可减到 。
问在不花费超过 的代价,该数组的最大值的最小值是多少。
解题思路
经典最大值最小,二分该值后判断一下代价是否超过即可。
神奇的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m, k; cin >> n >> m >> k; vector<array<int, 2>> a(n); int l = k - 1, r = 0; for(auto &i : a){ cin >> i[0] >> i[1]; r = max(r, i[0]); } auto check = [&](int x){ LL sum = 0; for(auto &i : a){ sum += 1ll * max(0, i[0] - x) * i[1]; } return sum <= m; }; while(l + 1 < r){ int mid = (l + r) >> 1; if (check(mid)){ r = mid; }else{ l = mid; } } cout << r << '\n'; return 0; }
LDAP
题目大意
给定个用户,用户有 唯一的,以及若干个属性。
现给定若干个查询指令,要求从小到大输出符合条件的 。
查询指令的如下
NON_ZERO_DIGIT = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" DIGIT = "0" / NON_ZERO_DIGIT NUMBER = NON_ZERO_DIGIT / (NON_ZERO_DIGIT DIGIT*) ATTRIBUTE = NUMBER VALUE = NUMBER OPERATOR = ":" / "~" BASE_EXPR = ATTRIBUTE OPERATOR VALUE LOGIC = "&" / "|" EXPR = BASE_EXPR / (LOGIC "(" EXPR ")" "(" EXPR ")")
比如1:2
表示查询属性1
值为2
的用户,1~2
表示查询有属性1
但属性1
值不为2
的用户。
然后以上可以用逻辑&
和|
组合,即&(1:2)(2:3)
表示查询属性1
值为2
且属性2
值为3
的用户,|(1:2)(3:1)
表示查询属性1
值为2
或属性3
值为1
的用户。
解题思路
按照BNF
写一个递归下降分析器就好了,一个简单的parser
。
每个判断语句的结果可以用一个bitset
储存起来,这样就可以直接&
和|
了。
神奇的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 2500; unordered_map<int, int> user[N]; int n, q; bool islogic(char s){ return s == '&' || s == '|'; } int find_rb(const string &s, int l){ int cnt = 0; assert(s[l] == '('); for(int i = l; i < int(s.size()); ++ i){ if (s[i] == '(') ++ cnt; else if (s[i] == ')') -- cnt; if (cnt == 0) return i; } assert(0); return -1; } int find_op(const string &s, int l){ for(int i = l; i < int(s.size()); ++ i){ if (s[i] == ':' || s[i] == '~') return i; } assert(0); return -1; } bitset<N> parse_basic(const string &s, int l, int r){ int op = find_op(s, l); int attr = atoi(s.substr(l, op - l).c_str()); int value = atoi(s.substr(op + 1, r - op).c_str()); bitset<N> result; if (s[op] == ':'){ for(int i = 0; i < n; ++ i){ if (user[i].find(attr) != user[i].end() && user[i][attr] == value) result.set(i); } }else{ for(int i = 0; i < n; ++ i){ if (user[i].find(attr) != user[i].end() && user[i][attr] != value) result.set(i); } } return result; } bitset<N> parse_expr(const string &s, int l, int r){ if (islogic(s[l])){ int rb = find_rb(s, l + 1); auto res1 = parse_expr(s, l + 2, rb - 1); auto res2 = parse_expr(s, rb + 2, r - 1); if (s[l] == '|') return res1 | res2; else return res1 & res2; }else{ return parse_basic(s, l, r); } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for(int i = 0; i < n; ++ i){ cin >> user[i][0]; int x; cin >> x; while(x--){ int k, v; cin >> k >> v; user[i][k] = v; } } cin >> q; while(q--){ string s; cin >> s; auto result = parse_expr(s, 0, s.size() - 1); vector<int> ans; for(int i = 0; i < n; ++ i){ if (result[i]) ans.push_back(user[i][0]); } sort(ans.begin(), ans.end()); for(auto &i : ans) cout << i << ' '; cout << '\n'; } return 0; }
星际网络II
题目大意
你需要维护由位二进制数组成的网络地址的分配。有以下种操作
1 id l r
:表示用户id
申请地址在l ~ r
范围内(包含l
和r
,下同)的一段连续地址块。若该范围未被分配,或者已部分分配给用户id
则成功,否则(已全部分配给用户id
或部分分配给其他用户)失败。2 s
:检查地址s
分配给哪个用户,未分配则输出0
3 l r
:检查地址范围l ~ r
是否完整地分配给某个用户,是则输出其id
,否则输出0
。
解题思路
典型的区间赋值+查询,如果地址不大,可以用柯朵莉树直接维护。
但由于n
高达512
位,不能用一个数表示,直接字符串比较会有的常数,因此就每32
位压成一个unsigned int
,用16个数表示一个地址,这样常数只有16
,然后就用柯朵莉树维护这个即可。
具体实现时在这个较小的数据 wa
了,赛场上看不出哪里错,但因为比较小,于是就单独写了这部分数据的简单化版,不知道改了哪里就过了(
神奇的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; constexpr int duan = 2; constexpr int wei = 16; int n, q; struct Address{ unsigned int val[wei]; Address(){ memset(val, 0, sizeof(val)); } bool operator <(const Address& o) const { for(int i = wei - 1; i >= 0; -- i){ if (val[i] != o.val[i]) return val[i] < o.val[i]; } return false; } bool operator <=(const Address& o) const { for(int i = wei - 1; i >= 0; -- i){ if (val[i] != o.val[i]) return val[i] < o.val[i]; } return true; } bool operator !=(const Address& o) const { for(int i = wei - 1; i >= 0; -- i){ if (val[i] != o.val[i]) return true; } return false; } bool operator ==(const Address& o) const { for(int i = wei - 1; i >= 0; -- i){ if (val[i] != o.val[i]) return false; } return true; } Address& operator ++(){ int pos = 0; while(true){ val[pos] ++; if (val[pos] == 0){ ++ pos; }else break; } return *this; } Address& operator --(){ int pos = 0; while(true){ if (val[pos] == 0){ val[pos] --; ++ pos; }else{ val[pos] --; break; } } return *this; } }; struct area{ Address l, r; int own; bool operator <(const area& o) const{ return l < o.l; } bool operator <=(const area& o) const{ return l <= o.l; } }; unsigned int tr(char s){ if (s >= '0' && s <= '9') return s - '0'; else return s - 'a' + 10; } unsigned int trans_int(const string& s, int l, int r){ unsigned int qwq = 0; for(int i = l; i < r; ++ i){ qwq = qwq * 16 + tr(s[i]); } return qwq; } Address trans(const string& s){ Address res; vector<unsigned int> tmp; for(int i = 0; i < int(s.size()); i += 4){ tmp.push_back(trans_int(s, i, i + 4)); ++ i; } int cnt = 0; unsigned int val = 0; int pos = 0; unsigned int base = 1; for(int i = tmp.size() - 1; i >= 0; -- i){ val = val + tmp[i] * base; ++ cnt; base <<= 16; if (cnt == duan){ res.val[pos] = val; val = 0; cnt = 0; base = 1; ++ pos; } } if (cnt) res.val[pos] = val; return res; } set<area> qq; set<pair<pair<int, int>, int>> small; const string zero = "0000"; const string maxx = "ffff"; Address max_address, min_address; int bigg; bool add(int id, const Address& l, const Address& r){ auto L = prev(qq.upper_bound({l, max_address})), R = qq.upper_bound({r, max_address}); int count = 0; bool allmy = true; for(auto i = L; i != R; i = next(i)){ if (i->own != 0 && i->own != id) return false; ++ count; allmy &= (i->own == id); } if (allmy) return false; auto LST = *L, RST = *prev(R); for(auto it = L; count > 0; it = qq.erase(it), -- count); Address insertL = l, insertR = r; if (LST.own != id){ if (LST.l != l){ auto newR = l; -- newR; LST.r = newR; qq.insert(LST); } }else{ insertL = LST.l; } if (RST.own != id){ if (RST.r != r){ auto newL = r; ++ newL; RST.l = newL; qq.insert(RST); } }else{ insertR = RST.r; } qq.insert({insertL, insertR, id}); return true; } int find(const Address& pos){ auto L = prev(qq.upper_bound({pos, max_address})); return L->own; } int check(const Address&l, const Address&r){ auto L = prev(qq.upper_bound({l, max_address})), R = prev(qq.upper_bound({r, max_address})); if (L == R) return L->own; else return 0; } bool addsmall(int id, const int& l, const int& r){ auto L = prev(small.upper_bound({{l, bigg}, 0})), R = small.upper_bound({{r, bigg}, 0}); int count = 0; bool allmy = true; for(auto i = L; i != R; i = next(i)){ if (i->second != 0 && i->second != id) return false; ++ count; allmy &= (i->second == id); } if (allmy) return false; auto LST = *L, RST = *prev(R); for(auto it = L; count > 0; it = small.erase(it), -- count); auto insertL = l, insertR = r; if (LST.second != id){ if (LST.first.first != l){ auto newR = l; -- newR; LST.first.second = newR; small.insert(LST); } }else{ insertL = min(insertL, LST.first.first); } if (RST.second != id){ if (RST.first.second != r){ auto newL = r; ++ newL; RST.first.first = newL; small.insert(RST); } }else{ insertR = max(insertR, RST.first.second); } small.insert({{insertL, insertR}, id}); return true; } int findsmall(const int& pos){ auto L = prev(small.upper_bound({{pos, bigg}, 0})); return L->second; } int checksmall(const int&l, const int&r){ auto L = prev(small.upper_bound({{l, bigg}, 0})), R = small.upper_bound({{r, bigg}, 0}); for(auto it = L; it != R; it = next(it)) if (it->second != L->second) return 0; return L->second; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> q; if (n == 16){ bigg = (1 << n); small.insert({{0, bigg - 1}, 0}); while(q--){ int op; cin >> op; if (op == 1){ int id; string l, r; cin >> id >> l >> r; auto L = trans_int(l, 0, 4), R = trans_int(r, 0, 4); if (addsmall(id, L, R)) cout << "YES" << '\n'; else cout << "NO" << '\n'; }else if (op == 2){ string s; cin >> s; auto S = trans_int(s, 0, 4); int res = findsmall(S); cout << res << '\n'; }else { string l, r; cin >> l >> r; auto L = trans_int(l, 0, 4), R = trans_int(r, 0, 4); int res = checksmall(L, R); cout << res << '\n'; } } }else{ string st, ed; for(int i = 0, up = n / 16; i < up; ++ i){ st += zero; ed += maxx; if (i != up - 1){ st += ":"; ed += ":"; } } min_address = trans(st); max_address = trans(ed); area tmp{min_address, max_address, 0}; qq.insert(tmp); while(q--){ int op; cin >> op; if (op == 1){ int id; string l, r; cin >> id >> l >> r; auto L = trans(l), R = trans(r); if (add(id, L, R)) cout << "YES" << '\n'; else cout << "NO" << '\n'; }else if (op == 2){ string s; cin >> s; auto S = trans(s); int res = find(S); cout << res << '\n'; }else { string l, r; cin >> l >> r; auto L = trans(l), R = trans(r); int res = check(L, R); cout << res << '\n'; } } } return 0; }
另解是地址离散化+权值线段树维护分配。
施肥
题目大意
一维数轴,给定条线段,问有多少个区间,满足可以选择若干条线段,恰好覆盖该区间。
解题思路
设表示能否恰好覆盖区间 ,然后就枚举左端点在的线段(设右端点为 ),那
最后答案就是数组中为 个数。
时间复杂度是 ,水了60
分。
神奇的代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; vector<vector<int>> ok(n, vector<int>(n, 0)); vector<array<int, 2>> seg(m); vector<vector<int>> edge(n); for(int i = 0; i < m; ++ i){ cin >> seg[i][0] >> seg[i][1]; -- seg[i][0]; -- seg[i][1]; edge[seg[i][0]].push_back(i); ok[seg[i][0]][seg[i][1]] = 1; } for(int i = 0; i < n; ++ i) sort(edge[i].begin(), edge[i].end(), [&](int x, int y){ return seg[x][1] < seg[y][1]; }); for(int i = n - 1; i >= 0; -- i) for(int j = i; j < n; ++ j){ for(auto &s : edge[i]){ if (seg[s][1] > j) break; for(int k = seg[s][0]; k <= seg[s][1] + 1 && k < n; ++ k){ ok[i][j] |= ok[k][j]; if (ok[i][j]) break; } if (ok[i][j]) break; } } LL ans = 0; for(int i = 0; i < n; ++ i) for(int j = i; j < n; ++ j) ans += ok[i][j]; cout << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/17269911.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步