2025.8.25 测试
CF786C Till I Collapse
\(\boldsymbol{根号分治}\) 。
发现 \(ans\le \frac{n}{k}\) ,考虑根号分治。
对于 \(k\le B\) ,直接暴力扫。
对于 \(k> B\) , 有 \(ans\le \frac{n}{B}\) ,且答案单调不升,二分以 \(ans\) 为答案的左右端点。
复杂度 \(O(nB+\frac{n^2}{B}\log n)\) ,取 \(B=\sqrt{n\log n}\) ,复杂度为 \(O(n\sqrt{n\log n})\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define cmx(x, y) std::max(x, y)
#define cmn(x, y) std::min(x, y)
#define gmx(x, y) x = std::max(x, y)
#define gmn(x, y) x = std::min(x, y)
const int _ = 2e5 + 7;
typedef double Db;
typedef long long ll;
typedef std::pair<int, int> PII;
int n, a[_], cnt[_], tot, stk[_], tp;
int B;
int add(int x) { if (!cnt[x]) stk[++tp] = x; ++cnt[x]; return tp; }
void cls() { while (tp) cnt[stk[tp--]] = 0; }
int bl(int k) {
cls(); int ans = 1;
lep(i, 1, n) if (add(a[i]) > k) ++ans, cls(), add(a[i]);
return ans;
}
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n; B = std::sqrt(n * log2(n));
lep(i, 1, n) std::cin >> a[i];
lep(k, 1, B) std::cout << bl(k) << ' ';
int l, r, md, p = B + 1;
rep(ans, bl(p), 1) {
l = p, r = n;
if (bl(p) != ans) continue;
while (l < r) { md = (l + r + 1) >> 1;
if (bl(md) == ans) l = md;
else r = md - 1;
}
lep(i, p, l) std::cout << ans << ' ';
p = l + 1;
}
return 0;
}
[ABC350G] Mediator
\(\boldsymbol{启发式合并}\) 。
维护每个点的父亲然后就可以 \(O(1)\) 处理询问,每次连边直接暴力跳一个端点的到根链维护父亲。
为了保证复杂度另外通过并查集维护树的大小并启发式合并。
复杂度 \(O(n\log n + q)\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define cmx(x, y) std::max(x, y)
#define cmn(x, y) std::min(x, y)
#define gmx(x, y) x = std::max(x, y)
#define gmn(x, y) x = std::min(x, y)
const int _ = 2e5 + 7;
const int mod = 998244353;
typedef double Db;
typedef long long ll;
typedef std::pair<int, int> PII;
int n, Q, fa[_], ans, pr[_], sz[_];
int mul(ll a, ll b) { return a * b >= mod ? a * b % mod : a * b; }
int fnd(int u) { return pr[u] == u ? u : pr[u] = fnd(pr[u]); }
void lk(int u, int v) {
if (sz[fnd(u)] > sz[fnd(v)]) std::swap(u, v);
int lst = v, p; pr[fnd(u)] = fnd(v), sz[fnd(v)] += sz[fnd(u)];
while (u) p = u, u = fa[u], fa[p] = lst, lst = p;
}
int qry(int u, int v) {
if (fa[u] == fa[v]) return fa[u];
if (u == fa[fa[v]]) return fa[v];
if (v == fa[fa[u]]) return fa[u];
return 0;
}
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n >> Q;
lep(i, 1, n) pr[i] = i, sz[i] = 1;
ll a, b, c;
while (Q--) {
std::cin >> a >> b >> c;
a = mul(a, ans + 1) & 1, b = mul(b, ans + 1) % n + 1, c = mul(c, ans + 1) % n + 1;
if (!a) lk(b, c);
else std::cout << (ans = qry(b, c)) << '\n';
}
return 0;
}
[AGC032D] Rotation Sort
\(\boldsymbol{线段树优化 DP}\) 。
对于一个元素,要么不进行操作,要么就只进行一次移动。
且不进行操作的元素一定构成了一个上升子序列。
考虑 \(DP\) ,设 \(f[i, j]\) 表示考虑了前 \(i\) 个元素,且不进行操作的元素最大值为 \(j\) 的方案数。
转移方程如下:
直接实现是 \(O(n^2)\) 的,发现是一个区间加以及前缀 \(\min\) 的形式,套个线段树优化做到 \(O(n\log n)\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define cmx(x, y) std::max(x, y)
#define cmn(x, y) std::min(x, y)
#define gmx(x, y) x = std::max(x, y)
#define gmn(x, y) x = std::min(x, y)
const int _ = 1e5 + 7;
typedef double Db;
typedef long long ll;
typedef std::pair<int, int> PII;
const ll inf = 1e17;
int n, x, y, a[_]; ll f[_ << 2], tag[_ << 2];
#define ls p << 1
#define rs p << 1 | 1
#define md ((s + t) >> 1)
void pu(int p) { f[p] = std::min(f[ls], f[rs]); }
void upd(int p, ll k) { f[p] += k, tag[p] += k; }
void pd(int p) { if (tag[p]) upd(ls, tag[p]), upd(rs, tag[p]), tag[p] = 0; }
void mdy(int l, int r, int s, int t, ll k, int p) {
if (r < s or t < l) return;
if (l <= s and t <= r) return upd(p, k); pd(p);
mdy(l, r, s, md, k, ls), mdy(l, r, md + 1, t, k, rs); pu(p);
}
void col(int d, int s, int t, ll k, int p) {
if (s == t) return f[p] = k, void(); pd(p);
d <= md ? col(d, s, md, k, ls) : col(d, md + 1, t, k, rs); pu(p);
}
ll qry(int l, int r, int s, int t, int p) {
if (r < s or t < l) return inf;
if (l <= s and t <= r) return f[p]; pd(p);
return std::min(qry(l, r, s, md, ls), qry(l, r, md + 1, t, rs));
}
#undef ls
#undef rs
#undef md
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n >> x >> y;
lep(i, 1, n) std::cin >> a[i];
mdy(1, n, 0, n, inf, 1);
lep(i, 1, n) {
col(a[i], 0, n, qry(0, a[i] - 1, 0, n, 1), 1);
mdy(0, a[i] - 1, 0, n, x, 1), mdy(a[i] + 1, n, 0, n, y, 1);
}
std::cout << qry(1, n, 0, n, 1) << '\n';
return 0;
}
[ABC397G] Maximize Distance
\(\boldsymbol{网络流建模}\) 。
原题意转化为,一张 \(0\) 边权的有向图,可以将至多 \(k\) 条边权改为 \(1\) ,最大化 \(1\sim n\) 最短路。
发现答案是 \(1\) 时很像最小割,考虑网络流。
枚举答案 \(d\) 判断是否可行。
状态 \((i, j)\) 表示到达 \(i\) 号点时最短路是 \(j\) ,我们希望让 \((n, 0\sim d-1)\) 和 \((1, 0)\) 不连通。
对于每条边,可以从 \((u, k)\) 通过 \(1\) 的边连向 \((v, k)\) ,表示可以通过 \(1\) 的代价让这条边边权变为 \(1\) 。
同时 \((u, k)\) 可以通过 \(inf\) 的边连向 \((v,k+1)\) ,表示如果上述的边被割掉,这两个状态也是可以转移的,且不能被割掉。
判断最小割是否 \(\le k\) 即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
#define cmx(x, y) std::max(x, y)
#define cmn(x, y) std::min(x, y)
#define gmx(x, y) x = std::max(x, y)
#define gmn(x, y) x = std::min(x, y)
const int _ = 1e5 + 7;
const int inf = 1e8;
typedef double Db;
typedef long long ll;
typedef std::pair<int, int> PII;
struct edge { int v, n, c; }e[_]; int H[_], cur[_], cnte = 1;
int n, m, k, u[_], v[_], ans;
int dep[_], S, T;
void add(int u, int v, int c) { e[++cnte] = { v, H[u], c }, H[u] = cnte; }
void Add(int u, int v, int c) { add(u, v, c), add(v, u, 0); }
inline int id(int i, int j) { return i + j * n; }
bool bfs() {
std::queue <int> q;
lep(i, S, T) dep[i] = 0; dep[S] = 1, q.push(S);
while (!q.empty()) {
int u = q.front(); q.pop();
if (u == T) return true;
for (int i = H[u], v = e[i].v; i; i = e[i].n, v = e[i].v)
if (e[i].c and !dep[v]) dep[v] = dep[u] + 1, q.push(v);
}
return false;
}
int dfs(int u, int flow) {
if (u == T or !flow) return flow;
int f, tot = 0;
for (int i = cur[u], v = e[i].v; i; i = e[i].n, v = e[i].v) { cur[u] = i;
if (e[i].c and dep[v] == dep[u] + 1) {
f = dfs(v, std::min(flow, e[i].c));
if (!f) { dep[v] = 0; continue; }
e[i].c -= f, e[i ^ 1].c += f, tot += f, flow -= f;
}
}
return tot;
}
bool ck(int d) {
S = id(1, 0), T = id(n + 1, d - 1);
lep(j, 0, d - 1) Add(id(n, j), T, inf);
lep(i, 1, m) {
lep(j, 0, d - 1) {
Add(id(u[i], j), id(v[i], j), 1);
if (j != d - 1) Add(id(u[i], j), id(v[i], j + 1), inf);
}
}
int res = 0;
while (bfs()) {
lep(i, S, T) cur[i] = H[i];
res += dfs(S, inf);
}
lep(i, S, T) H[i] = 0; cnte = 1;
return res <= k;
}
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n >> m >> k;
lep(i, 1, m) std::cin >> u[i] >> v[i];
lep(i, 1, n) if (!ck(i)) { std::cout << i - 1 << '\n'; break; }
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

需要赶紧提升了
浙公网安备 33010602011771号