CodeForces 2009G Yunli's Subarray Queries 题解
云璃!
高质量 Div.4,吊打某些 Div.2 Only / Edu / Div.3。
本题是下面四道题目的有机结合,优雅且经典。
Luogu P4168 [Violet] 蒲公英 | Luogu P1997 faebdc 的烦恼 | Luogu P3203 [HNOI2010] 弹飞绵羊 | Luogu P3246 [HNOI2016] 序列
题给函数
对于 easy version,显然全部修改为区间众数是最优的,回滚莫队求区间众数即可,时间复杂度
对于 hard version,对
考虑求这个值,对
每次暴力跳会超时,考虑分块处理出第一次跳出当前块会到达的位置和这时产生的总贡献,时间复杂度优化到
对于 extreme version,所求为
仍然考虑莫队,需要快速计算移动端点带来的贡献,即一段区间的所有前缀 / 后缀的最小值之和。
上一个 subtask 中求出了
容易发现前缀的贡献是可以差分的,所以设前缀贡献
但是直接差分错完了,因为前缀和中贡献是按整段计算的。设
于是莫队的移动端点可以在
然后因为常数过大被卡了。(垃圾 math.h)
稍微精细实现一下,用单调栈代替二分求
好像官解不是这样做的,了解更多做法请见 官方题解。
接下来是很长的代码。
// easy version #pragma GCC optimize("Ofast") #include <algorithm> #include <iostream> using namespace std; const int block_size = 450; int n, k, q; int a[200005]; int b[200005]; int rs[200005]; int val[200005], len; struct node { int l, r, id; inline bool operator<(const node &nd) const { return b[l] == b[nd.l] ? r < nd.r : b[l] < b[nd.l]; } } qs[200005]; int cnt[200005]; int cnt2[200005]; int ans[200005]; static inline void solve() { cin >> n >> k >> q; for (int i = 1; i <= n; ++i) { cin >> a[i]; a[i] -= i; val[++len] = a[i]; b[i] = (i - 1) / block_size + 1; rs[b[i]] = i; } sort(val + 1, val + len + 1); len = unique(val + 1, val + len + 1) - val - 1; for (int i = 1; i <= n; ++i) a[i] = lower_bound(val + 1, val + len + 1, a[i]) - val; for (int i = 1; i <= len; ++i) cnt[i] = 0; for (int i = 1; i <= q; ++i) { cin >> qs[i].l >> qs[i].r; qs[i].id = i; } sort(qs + 1, qs + q + 1); int curl = 1; int curr = 0; int las = 0; int now = 0; for (int t = 1; t <= q; ++t) { auto [l, r, id] = qs[t]; if (b[l] == b[r]) { int ret = 0; for (int i = l; i <= r; ++i) { ++cnt2[a[i]]; if (cnt2[a[i]] > cnt2[ret]) ret = a[i]; } ans[id] = r - l + 1 - cnt2[ret]; for (int i = l; i <= r; ++i) --cnt2[a[i]]; continue; } if (b[l] != las) { while (curr > rs[b[l]]) --cnt[a[curr--]]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; now = 0; las = b[l]; } while (curr < r) { ++cnt[a[++curr]]; if (cnt[a[curr]] > cnt[now]) now = a[curr]; } int ret = now; while (curl > l) { ++cnt[a[--curl]]; if (cnt[a[curl]] > cnt[ret]) ret = a[curl]; } ans[id] = r - l + 1 - cnt[ret]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; } for (int i = 1; i <= q; ++i) cout << ans[i] << endl; } signed main() { #ifndef ONLINE_JUDGE freopen("CF2009G.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T; cin >> T; while (T--) solve(); cout << flush; return 0; }
// hard version #pragma GCC optimize("Ofast") #include <algorithm> #include <iostream> #include <math.h> using namespace std; const int block_size = 450; int n, k, q; int a[200005]; int b[200005]; int ls[200005]; int rs[200005]; int val[200005], len; struct node { int l, r, id; inline bool operator<(const node &nd) const { return b[l] == b[nd.l] ? r < nd.r : b[l] < b[nd.l]; } } qs[200005]; int cnt[200005]; int cnt2[200005]; int c[200005]; int st[200005][20]; static inline int query(int l, int r) { int len = (int)log2(r - l + 1); int x = st[l][len]; int y = st[r - (1 << len) + 1][len]; return c[x] < c[y] ? x : y; } int nxt[200005]; int rail[200005]; long long w[200005]; static inline void solve() { // READ & BLOCK & HASH cin >> n >> k >> q; for (int i = 1; i <= n; ++i) { cin >> a[i]; a[i] -= i; val[++len] = a[i]; b[i] = (i - 1) / block_size + 1; rs[b[i]] = i; } for (int i = b[1]; i <= b[n]; ++i) ls[i] = rs[i - 1] + 1; sort(val + 1, val + len + 1); len = (int)(unique(val + 1, val + len + 1) - val - 1); for (int i = 1; i <= n; ++i) a[i] = (int)(lower_bound(val + 1, val + len + 1, a[i]) - val); for (int i = 1; i <= len; ++i) cnt[i] = 0; for (int i = 1; i <= n - k + 1; ++i) { qs[i].l = i; qs[i].r = i + k - 1; qs[i].id = i; } // MO sort(qs + 1, qs + n - k + 2); int curl = 1; int curr = 0; int las = 0; int now = 0; for (int t = 1; t <= n - k + 1; ++t) { auto [l, r, id] = qs[t]; if (b[l] == b[r]) { int ret = 0; for (int i = l; i <= r; ++i) { ++cnt2[a[i]]; if (cnt2[a[i]] > cnt2[ret]) ret = a[i]; } c[id] = r - l + 1 - cnt2[ret]; for (int i = l; i <= r; ++i) --cnt2[a[i]]; continue; } if (b[l] != las) { while (curr > rs[b[l]]) --cnt[a[curr--]]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; now = 0; las = b[l]; } while (curr < r) { ++cnt[a[++curr]]; if (cnt[a[curr]] > cnt[now]) now = a[curr]; } int ret = now; while (curl > l) { ++cnt[a[--curl]]; if (cnt[a[curl]] > cnt[ret]) ret = a[curl]; } c[id] = r - l + 1 - cnt[ret]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; } // ST for (int i = 1; i <= n - k + 1; ++i) st[i][0] = i; for (int j = 1; j <= 18; ++j) { for (int i = 1; i + (1 << j) - 1 <= n - k + 1; ++i) { int x = st[i][j - 1]; int y = st[i + (1 << (j - 1))][j - 1]; if (c[x] < c[y]) st[i][j] = x; else st[i][j] = y; } } // BLOCK: NEXT RAIL for (int i = 1; i <= n - k + 1; ++i) { b[i] = (i - 1) / block_size + 1; rs[b[i]] = i; } for (int i = b[1]; i <= b[n - k + 1]; ++i) ls[i] = rs[i - 1] + 1; for (int i = 1; i <= n - k + 1; ++i) { int l = i; int r = n - k + 1; int ret = n - k + 2; while (l <= r) { int mid = (l + r) >> 1; if (c[query(i, mid)] < c[i]) { ret = mid; r = mid - 1; } else { l = mid + 1; } } nxt[i] = ret; } for (int i = 1; i <= n - k + 1; ++i) { int ret = i; long long ans = 0; while (ret <= rs[b[i]]) { ans += (long long)(nxt[ret] - ret) * c[ret]; ret = nxt[ret]; } rail[i] = ret; w[i] = ans; } // QUERY while (q--) { int l, r; cin >> l >> r; r = r - k + 1; int cur = l; long long ans = 0; while (rail[cur] <= r) { ans += w[cur]; cur = rail[cur]; } while (nxt[cur] <= r) { ans += (long long)(nxt[cur] - cur) * c[cur]; cur = nxt[cur]; } ans += (long long)(r - cur + 1) * c[cur]; cout << ans << endl; } } signed main() { #ifndef ONLINE_JUDGE freopen("CF2009G.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T; cin >> T; while (T--) solve(); cout << flush; return 0; }
// extreme version #include <algorithm> #include <iostream> using namespace std; const int block_size = 450; const int lim = 2e5; int lg[lim + 5]; int n, k, q; int a[200005]; int b[200005]; int ls[200005]; int rs[200005]; int val[200005], len; struct node { int l, r, id; inline bool operator<(const node &nd) const { return (b[l] == b[nd.l]) ? ((b[l] & 1) ? (r < nd.r) : (r > nd.r)) : (b[l] < b[nd.l]); } } qs[200005]; int cnt[200005]; int cnt2[200005]; int c[200005]; int stk[200005], tail; int st[200005][20]; static inline int query(int l, int r) { int len = lg[r - l + 1]; int x = st[l][len]; int y = st[r - (1 << len) + 1][len]; return c[x] < c[y] ? x : y; } int pre[200005], nxt[200005]; long long f[200005], g[200005]; long long ans[200005]; static inline void solve() { // READ & BLOCK & HASH cin >> n >> k >> q; for (int i = 1; i <= n; ++i) { cin >> a[i]; a[i] -= i; val[++len] = a[i]; b[i] = (i - 1) / block_size + 1; rs[b[i]] = i; } for (int i = b[1]; i <= b[n]; ++i) ls[i] = rs[i - 1] + 1; sort(val + 1, val + len + 1); len = (int)(unique(val + 1, val + len + 1) - val - 1); for (int i = 1; i <= n; ++i) a[i] = (int)(lower_bound(val + 1, val + len + 1, a[i]) - val); for (int i = 1; i <= len; ++i) cnt[i] = 0; // MO { int curl = 1; int curr = 0; int las = 0; int now = 0; for (int t = 1; t <= n - k + 1; ++t) { int l = t; int r = t + k - 1; int id = t; if (b[l] == b[r]) { int ret = 0; for (int i = l; i <= r; ++i) { ++cnt2[a[i]]; if (cnt2[a[i]] > cnt2[ret]) ret = a[i]; } c[id] = r - l + 1 - cnt2[ret]; for (int i = l; i <= r; ++i) --cnt2[a[i]]; continue; } if (b[l] != las) { while (curr > rs[b[l]]) --cnt[a[curr--]]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; now = 0; las = b[l]; } while (curr < r) { ++cnt[a[++curr]]; if (cnt[a[curr]] > cnt[now]) now = a[curr]; } int ret = now; while (curl > l) { ++cnt[a[--curl]]; if (cnt[a[curl]] > cnt[ret]) ret = a[curl]; } c[id] = r - l + 1 - cnt[ret]; while (curl < rs[b[l]] + 1) --cnt[a[curl++]]; } } // PRE-PROCESS for (int i = 1; i <= n - k + 1; ++i) { b[i] = (i - 1) / block_size + 1; st[i][0] = i; } for (int j = 1; j <= 18; ++j) { for (int i = 1; i + (1 << j) - 1 <= n - k + 1; ++i) { int x = st[i][j - 1]; int y = st[i + (1 << (j - 1))][j - 1]; if (c[x] < c[y]) st[i][j] = x; else st[i][j] = y; } } stk[++tail] = 0; for (int i = 1; i <= n - k + 1; ++i) { while (c[stk[tail]] > c[i]) --tail; pre[i] = stk[tail]; stk[++tail] = i; } stk[tail = 1] = n - k + 2; for (int i = n - k + 1; i; --i) { while (c[stk[tail]] > c[i]) --tail; nxt[i] = stk[tail]; stk[++tail] = i; } for (int i = 1; i <= n - k + 1; ++i) f[i] = f[pre[i]] + (long long)c[i] * (i - pre[i]); for (int i = n - k + 1; i; --i) g[i] = g[nxt[i]] + (long long)c[i] * (nxt[i] - i); // QUERY for (int i = 1; i <= q; ++i) { cin >> qs[i].l >> qs[i].r; qs[i].r = qs[i].r - k + 1; qs[i].id = i; } sort(qs + 1, qs + q + 1); { int curl = 1; int curr = 0; long long now = 0; for (int i = 1; i <= q; ++i) { auto [l, r, id] = qs[i]; while (curl > l) { --curl; int p = query(curl, curr); now += g[curl] - g[p] + (long long)c[p] * (curr - p + 1); } while (curr < r) { ++curr; int p = query(curl, curr); now += f[curr] - f[p] + (long long)c[p] * (p - curl + 1); } while (curl < l) { int p = query(curl, curr); now -= g[curl] - g[p] + (long long)c[p] * (curr - p + 1); ++curl; } while (curr > r) { int p = query(curl, curr); now -= f[curr] - f[p] + (long long)c[p] * (p - curl + 1); --curr; } ans[id] = now; } } for (int i = 1; i <= q; ++i) cout << ans[i] << endl; } signed main() { #ifndef ONLINE_JUDGE freopen("CF2009G.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); for (int i = 2; i <= lim; ++i) lg[i] = lg[i >> 1] + 1; int T; cin >> T; while (T--) solve(); cout << flush; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现