voidsolve() { ans = 0; memset(cnt, 0, sizeof(cnt)); cin >> n; for (int i = 1; i <= n; i++) { int x, y, z; cin >> x >> y >> z; a[i].a[1] = {x, 1}; a[i].a[2] = {y, 2}; a[i].a[3] = {z, 3}; sort(a[i].a + 1, a[i].a + 1 + 3); cnt[a[i].a[3].second]++; a[i].dif = a[i].a[3].first - a[i].a[2].first; ans += a[i].a[3].first; } sort(a + 1, a + 1 + n, cmp); int X = -1, Y = -1; for (int i = 1; i <= 3; i++) { if (cnt[i] > n / 2) { X = cnt[i]; Y = i; break; } } if (X != -1) { X -= n / 2; for (int i = 1; i <= n && X > 0; i++) { if (a[i].a[3].second == Y) { ans -= a[i].dif; X--; } } } cout << ans << endl; }
intmain() { cin >> T; while (T--) solve(); return0; }
D 第 K 小的和
Solution
注意到 x 越大,排名越大。故二分找第 k 小的数。
首先对 a,b 排序。对二分的值 x,枚举 ai,二分求出在 b 中有 toti 个元素小于等于 x−ai。
boolcheck(ll x) { ll res = 0; for (int i = 1; i <= n; i++) { if (a[i] + b[1] > x) break; ll tot = upper_bound(b + 1, b + 1 + m, x - a[i]) - b - 1; res += tot; } return (res >= k); }
intmain() { cin >> n >> m >> k; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= m; i++) cin >> b[i]; sort(a + 1, a + 1 + n); sort(b + 1, b + 1 + m); ll l = 2, r = 2e9, mid; while (l <= r) { mid = (l + r) >> 1; if (check(mid)) { ans = mid; r = mid - 1; } else l = mid + 1; } cout << ans << endl; return0; }
E 成绩统计
Solution
注意到检查的同学越多,越可能满足条件。故我们对检查的同学数量 x 进行二分。
因为数字互相越接近,方差越小,所以对 v 的区间 [1,x] 进行排序,并枚举连续 k 个数,判断是否满足条件。
ll n, ans = -1, a[N], b[N], k, T; __int128 s1[N], s2[N];
boolcheck(ll x) { __int128 res; for (int i = 1; i <= x; i++) b[i] = a[i]; sort(b + 1, b + 1 + x); // 前缀和预处理出两组求和 for (int i = 1; i <= x; i++) { s1[i] = s1[i - 1] + b[i]; s2[i] = s2[i - 1] + b[i] * b[i]; } for (int i = k; i <= x; i++) { // 最后求出的公式 res = k * (s2[i] - s2[i - k]) - (s1[i] - s1[i - k]) * (s1[i] - s1[i - k]); if (res < (__int128)k * k * T) return1; } return0; }
intmain() { cin >> n >> k >> T; for (int i = 1; i <= n; i++) cin >> a[i]; ll l = k, r = n, mid; while (l <= r) { mid = (l + r) >> 1; if (check(mid)) r = mid - 1, ans = mid; else l = mid + 1; } cout << ans << endl; return0; }
int val[N], n, m, ans[N]; vector<string> str[N], S;
voidsolve(int k) { for (int i = 1; i <= n; i++) { string s_type = ""; int s_pos; // 找通配符的位置 for (int j = 0; j < str[i].size(); j++) { if (str[i][j] == "*" || str[i][j] == "**") { s_type = str[i][j]; s_pos = j; break; } } if (s_type.empty()) { // 不含通配符 if (str[i] == S) ans[k] = max(ans[k], val[i]); } elseif (s_type == "*") { // 通配符 * if (str[i].size() != S.size()) continue; int flg = 1; for (int j = 0; j < S.size(); j++) { if (S[j] != str[i][j] && str[i][j] != "*") { flg = 0; break; } } if (flg) ans[k] = max(ans[k], val[i]); } else { // 通配符 ** int flg = 1, suf = str[i].size() - s_pos - 1; if (s_pos + suf > S.size()) continue; // 比较前缀 for (int j = 0; j < s_pos; j++) { if (S[j] != str[i][j]) { flg = 0; break; } } // 比较后缀 for (int j = 1; j <= suf; j++) { if (S[S.size() - j] != str[i][str[i].size() - j]) { flg = 0; break; } } if (flg) ans[k] = max(ans[k], val[i]); } } }
intmain() { cin >> n; for (int i = 1; i <= n; i++) { string seg, s; cin >> val[i] >> s; // 按斜杠分割路径,存在字符串vector里面 for (int j = 1; j < s.size(); j++) { if (s[j] == '/') { str[i].push_back(seg); seg = ""; } else seg += s[j]; } str[i].push_back(seg); } cin >> m; for (int i = 1; i <= m; i++) { string seg, s; S.clear(); cin >> s; for (int j = 1; j < s.size(); j++) { if (s[j] == '/') { S.push_back(seg); seg = ""; } else seg += s[j]; } S.push_back(seg); solve(i); } for (int i = 1; i <= m; i++) { if (!ans[i]) cout << "SAFE\n"; else cout << "ALERT: " << ans[i] << endl; } return0; }
G 上升序列构造
Solution
贪心 + 构造。要在保证后一个数大于前一个数的同时,后一个数尽可能小。
数字的位数可能会到 105000 级别,考虑转化成字符串处理。
设前一个字符串(已经加 0)为 a,长度 la。当前字符串为 b,长度 lb。分类讨论:
若 la<lb:不用加 0。
若 la=lb 且 a<b :不用加 0。
否则,枚举 i 计算要使得 a 长为 i 的前缀与 b 的前缀相同,需要加多少个 0 改造 b 的前缀,且改造后 bi+1>ai+1 成立。若存在 i 使得不等式成立,则在改造后的 bi+1 后插入剩余的 0 使得 la=lb。否则在 b 的第二位开始插入若干个 0,使得 lb=la+1。