Loading

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\) 的方案数。
转移方程如下:

\[\begin{align*} f[i + 1, j]&\leftarrow f[i, j] + A \text{ if } j<a_{i+1}\\ f[i + 1, a_{i +1}] &\leftarrow f[i, j]\text{ if } j<a_{i+1}\\ f[i + 1, j]&\leftarrow f[i, j] + B\text{ if } j>a_{i+1}\\ \end{align*} \]

直接实现是 \(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;
}

posted @ 2025-08-25 16:04  qkhm  阅读(16)  评论(0)    收藏  举报