Codeforces Round 887 (Div. 1)
Codeforces Round 887 (Div. 1)
A
先来个二分。注意到这样一件事:考虑是 失效的最小时间 ,发现 有单调性。所以从大到小考虑 ,每次相当于将二分的值减去一个值,复杂度 。
code
const int N = 2e5 + 10; int n; ll k; ll a[N]; ll calc(ll x) { ll res = 0; for(int i = n; i >= 1; --i) { ll d; if(x < a[i]) d = 0; else d = min(k, (x - a[i]) / i + 1); x -= d; res += d; } return res; } void solve() { cin >> n >> k; for(int i = 1; i <= n; ++i) cin >> a[i]; ll ans = 0; for(ll l = 0, r = 2e14; l <= r; ) { ll mid = (l + r) / 2; if(calc(mid) >= mid) ans = mid + 1, l = mid + 1; else r = mid - 1; } cout << ans << '\n'; }
B
不难发现所求序列的绝对值是一个 到 的排列。画个图可以发现,如果我们按照 的绝对值从大到小确定这个排列,那么限制是好描述的。设 在 中系数为 。那么有 。这样,我们发现若存在合法解,则解唯一。所以可以直接逐位确定。
code
void solve() { cin >> n; for(int i = 1; i <= n; ++i) cin >> a[i].first, a[i].second = i; sort(a + 1, a + n + 1); for(int i = n, l = 1, r = n, pre = 0; i >= 1; --i) { if(a[r].first > i) { cout << "NO\n"; return ; } else if(a[l].first == 0 && a[r].first == i) { cout << "NO\n"; return ; } else if(a[r].first == i) { ++pre, ans[a[r].second] = i; --a[l].first; if(l < --r) a[r].first -= pre; } else if(a[l].first == 0) { ans[a[l].second] = -i; if(++l < r) a[l].first -= pre; } else { cout << "NO\n"; return ; } } cout << "YES\n"; for(int i = 1; i <= n; ++i) cout << ans[i] << " "; cout << '\n'; }
C
题意转化为初始 ,每次对 区间 ,最后使得 。不难发现若最终 确定,则答案为 。
设 ,考虑我们可以干啥,发现我们是答案变优的手段是将若干个 增大 ,这样就会消去某些位置的贡献。发现最终 ,否则不调整一定不劣。考虑限制,发现类似括号序,每个前缀中 的数量大于等于消去贡献位置的数量。发现这玩意不好 dp,于是考虑贪心。不难得到费用流模型:对 连边 ,对 连边 ,任意 连边 。直接使用小根堆模拟即可。
code
void solve() { cin >> n >> k; for(int i = 1; i <= n; ++i) { cin >> a[i]; if(a[i] == k) a[i] = 0; } priority_queue<int, vector<int>, greater<int>> q; ll sum = 0; for(int i = 1; i <= n; ++i) { int d = a[i] - a[i - 1]; sum += max(d, 0); if(d < 0) { q.push(k + d); } else if(q.size()) { int x = q.top(); if(x < d) { sum += x - d; q.pop(), q.push(d); } } } cout << sum << '\n'; }
D
暴力 dp,打表发现可以有性质,直接做就行了。
code
const int N = 2e5 + 10; int n, k; string s; struct info { int l, r, p; info operator + (const int &x) const { return {l + x, r + x, p == -1 ? -1 : p + x}; } info operator + (const info &x) const { if(r + 2 == x.l) return {l, x.r, r + 1}; if(x.r + 2 == l) return {x.l, r, x.r + 1}; if(~p && p < x.l) return {l, max(r, x.r), p}; if(~p && p > x.r) return {min(l, x.l), r, p}; if(~x.p && x.p < l) return {x.l, max(r, x.r), x.p}; if(~x.p && x.p > r) return {min(l, x.l), x.r, x.p}; return {min(l, x.l), max(r, x.r), p == x.p ? p : -1}; } bool check(int x) { return l <= x && x <= r && x != p; } }f[N][2]; string ans; void out(int n, int c) { k -= (s[n] - 'A' != c); for(int nc = 0; nc < 2; ++nc) { if(f[n - 1][nc].check(k - (c != nc))) { k -= (c != nc); out(n - 1, nc); break; } } ans += c + 'A'; } void solve() { cin >> n >> k >> s; f[0][0] = {s[0] == 'B', s[0] == 'B', -1}; f[0][1] = {s[0] == 'A', s[0] == 'A', -1}; for(int i = 1; i < n; ++i) { k -= (s[i] != s[i - 1]); if(s[i] == 'A') { f[i][0] = (f[i - 1][0] + (f[i - 1][1] + 1)); f[i][1] = ((f[i - 1][0] + 1) + f[i - 1][1]) + 1; } else { f[i][0] = (f[i - 1][0] + (f[i - 1][1] + 1)) + 1; f[i][1] = ((f[i - 1][0] + 1) + f[i - 1][1]); } } ans.clear(); if(f[n - 1][0].check(k)) { cout << "YES\n"; out(n - 1, 0); cout << ans << '\n'; return ; } else if(f[n - 1][1].check(k)) { cout << "YES\n"; out(n - 1, 1); cout << ans << '\n'; } else { cout << "NO\n"; } }
E
不难发现,如果出现凸这种形状,那么大区间是没用的。这样可以先求出所有实际上有用的区间,那么这些区间的左右端点是不能动的,其他部分可以乱搞。
感觉一下,认为最多出现一种原序列没有的数,证明很简单,如果有两种那么把小的变成大的依然满足限制。我们考虑怎么安排新的这种数,发现需要让新区间包含一个值比他大的小区间。
考虑如果所有区间都已确定,那么接下来每个位置只需要填包含这个区间的最大的数即可,先不管新的数求一边。现在剩下的问题就是如何确定新的数的区间。我们考虑枚举包含的那个比他大的区间,不难发现会如果某一侧有比新数小的数那么会直接选了,如果没有则会选最小的。可以用依托数据结构和 vector
上二分维护做到 。
写一个下午发现做法一直假,麻了。在细节上卡了整整三天才过。细节是什么呢?如果我们去掉某些区间后,一些位置直接贪得不到一个确定的数,那么我们必然需要新数,否则可以不使用新数。
code
const int N = 1e6 + 10; int n, m, o[N], b[N]; struct info { int l, r, x; }a[N]; map<int, int> fl, fr; struct BIT { int tr[N]; int lowbit(int x) { return x & -x; } void clear() { for(int i = 1; i <= n; ++i) tr[i] = 0; } void add(int x, int v) { for(; x <= n; x += lowbit(x)) tr[x] = max(tr[x], v); } int query(int x) { int res = 0; for(; x; x -= lowbit(x)) res = max(res, tr[x]); return res; } }s; int occ[N]; vector<int> vec; void clear() { fl.clear(), fr.clear(); for(int i = 1; i <= n; ++i) b[i] = occ[i] = 0; vec.clear(), s.clear(); } void solve() { clear(); cin >> n; for(int i = 1; i <= n; ++i) { cin >> o[i]; if(!fl.count(o[i])) fl[o[i]] = i; fr[o[i]] = i; } m = 0; for(auto [x, l] : fl) { int r = fr[x]; a[++m] = {l, r, x}; } sort(a + 1, a + m + 1, [](info x, info y) { return x.l == y.l ? x.r < y.r : x.l > y.l; }); vector<int> vv; int tmp = m; m = 0; for(int i = 1; i <= tmp; ++i) { int l = a[i].l, r = a[i].r, x = a[i].x; int p = s.query(r); if(p < x) { a[++m] = a[i]; s.add(r, x); occ[l] = occ[r] = m; vv.eb(x); } } sort(vv.begin(), vv.end()); int lst = 0; for(int x : vv) { if(lst != x - 1) vec.eb(x - 1); lst = x; } set<int, greater<int>> se; se.insert(0); vector<pii> vb; vector<int> vmn, vmx; for(int i = 1; i <= n; ++i) { if(occ[i]) { int x = a[occ[i]].x; b[i] = x; if(se.find(x) != se.end()) se.erase(x); else if(a[occ[i]].r > a[occ[i]].l) se.insert(x); } else { b[i] = *se.begin(); vb.emplace_back(mp(b[i], i)); } } sort(vb.begin(), vb.end()); vmn.resize(vb.size()), vmx.resize(vb.size()); for(int i = 0, mn = n + 1, mx = 0; i < sz(vb); ++i) vmn[i] = -(mn = min(mn, vb[i].second)), vmx[i] = mx = max(mx, vb[i].second); auto qmn = [&](int x) { int rk = lower_bound(vmn.begin(), vmn.end(), -x) - vmn.begin(); if(rk == sz(vb)) return -1; return vb[rk].second; }; auto qmx = [&](int x) { int rk = lower_bound(vmx.begin(), vmx.end(), x) - vmx.begin(); if(rk == sz(vb)) return -1; return vb[rk].second; }; vec.emplace_back(-1); sort(vec.begin(), vec.end()); auto get = [&](int x) { return *(lower_bound(vec.begin(), vec.end(), x + 1) - 1); }; ll ms = 0; for(int i = 1; i <= n; ++i) if(b[i] == 0) ms = -1e18; int L = -1, R = -1, w = -1; ll sum = 0; int mn = n + 1, mx = 0; sort(a + 1, a + m + 1, [](info x, info y) { return x.x < y.x; }); for(int i = 1, p = 0; i <= m; ++i) { int x = a[i].x, v = get(x); if(v <= 0) continue; while(p < sz(vb) && vb[p].first <= v) { mn = min(mn, vb[p].second), mx = max(mx, vb[p].second); sum += vb[p++].first; } ll t = 1ll * p * v - sum; int lp = 0, rp = 0; if(mn > a[i].l) { int ql = qmn(a[i].l); if(ql == -1) continue; assert(b[ql] > v); t = t - b[ql] + v, lp = ql; } if(mx < a[i].r) { int qr = qmx(a[i].r); if(qr == -1) continue; assert(b[qr] > v); t = t - b[qr] + v, rp = qr; } if(t > ms) { ms = t, w = v; L = lp, R = rp; } } for(int i = 1; i <= n; ++i) if(occ[i]) cout << b[i] << ' '; else { if(i == L || i == R) cout << w << ' '; else cout << max(w, b[i]) << ' '; } cout << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int T; cin >> T; while(T--) solve(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2022-12-29 「ZJOI2022」众数