Loading

APIO 2024

A-September

模拟题。

取出 \(m\) 个人里包含相同元素的段,再判断删掉的是不是都是叶子就好,时间复杂度 \(O(nm)\)

#include <bits/stdc++.h>

using namespace std;

int solve(int n, int m, vector<int> fa, vector<vector<int>> s) {
	int ans = 0, cnt = 0;
	vector<int> d(n), c(n), del(n);
	auto upd = [&](int j) -> void {
		for (int i = 0; i < m; i++) {
			if (!c[s[i][j]]) cnt++;
			if (++c[s[i][j]] == m) {
				del[s[i][j]] = 1;
				for (int u = s[i][j]; !d[u] && del[u]; d[u = fa[u]]--) cnt--;
			}
		}
	};

	for (int i = 1; i < n; i++) d[fa[i]]++;
	for (int l = 0, r; l < n - 1; ans++, l = r + 1) for (upd(r = l); cnt; upd(++r));
	return ans;
}

B-Train

DP。

考虑把边看做点,把点看做边,按边的 \(A_i\) 从小到大遍历:

\(f_i\) 为到第 \(i\) 条边的起点的最小花费。

\[f_i = \min_{Y_j = X_i, B_j \le A_i} \{f_j + T_{X_i} \times cnt_{B_j + 1, A_i - 1} \} + C_i \]

其中 \(cnt_{l, r}\) 表示在满足 \([L_i, R_i] \sube [l, r]\) 的餐 \(i\) 的个数,可以用主席树快速计算。

时间复杂度 \(\mathcal O(m^2 \log w)\)

还不够,继续优化:

考虑在每个终点建一个单调队列,这样转移就是 \(\mathcal O(m \log w)\) 的了,难点在插入每顿餐的时候维护好这些单调队列。

因为在同一个星球上吃一顿饭的费用是确定的,所以对于每一个单调队列里的每一个 \(f_j + T_{X_i} \times cnt_{B_j + 1, A_i - 1}\),我们可以算出来它什么时候会把它前面的元素 \(f_k + T_{X_i} \times cnt_{B_k + 1, A_i - 1}\) 爆掉,即在该星球上吃的饭中 \(l_i \in [B_j + 1, B_k]\) 的个数等于 \(\left\lceil \dfrac{f(k) - f(j)}{T_{X_i}} \right\rceil\) 时。

然后我们标记这顿饭,并在将它考虑进来的时候把 \(k\) 删掉,因为随着 \(A_i\) 变大,\(cnt_{B_k + 1, A_i - 1} - cnt_{B_j + 1, A_i - 1}\) 不降,故此时 \(j\)\(k\) 更优,未来一定 \(j\)\(k\) 更优。

标记需要支持查询第 \(k\) 大,在前面的主席树上就能解决。

时间复杂度 \(\mathcal O((m + w)\log w)\)

#include <bits/stdc++.h>
#include "train.h"

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vint;

constexpr int N = 1e5 + 10, TN = N * 40, INF = 1e9;

int n, m, w, tot, rt[TN], id[N], hd[N], tl[N];
ll f[N];
bool vis[N];

struct Train {
	int x, y, a, b, c;
	bool operator<(const Train &rhs) const {return a < rhs.a;}
} tr[N];

struct Meal {int l, r, rk;} ml[N], mr[N];
bool cmpl(const Meal &lhs, const Meal &rhs) {return lhs.l != rhs.l ? lhs.l < rhs.l : lhs.r < rhs.r;}
bool cmpr(const Meal &lhs, const Meal &rhs) {return lhs.r != rhs.r ? lhs.r < rhs.r : lhs.l < rhs.l;}

vint c[N], to[N], pre[N];

priority_queue<pii, vector<pii>, greater<pii>> q;

namespace PST {
	int tot;
	struct Seg {int w, ls, rs;} t[TN];

	inline int newt(int rt) {t[++tot] = t[rt]; return tot;}
	
	void upd(int rt0, int &rt, int l, int r, int x) {
		rt = newt(rt0), t[rt].w++;
		if (l == r) return;
		int mid = (l + r) >> 1;
		x <= mid ? upd(t[rt0].ls, t[rt].ls, l, mid, x) : upd(t[rt0].rs, t[rt].rs, mid + 1, r, x);
	}

	int query(int pos, int l, int r, int x, int y) {
		if (x <= l && r <= y) return t[pos].w;
		int mid = (l + r) >> 1, res = 0;
		if (x <= mid) res = query(t[pos].ls, l, mid, x, y);
		if (y > mid) res += query(t[pos].rs, mid + 1, r, x, y);
		return res;
	}

	int kth(int rx, int ry, int l, int r, int k) {
		if (l == r) return l;
		int mid = (l + r) >> 1, lsz = t[t[ry].ls].w - t[t[rx].ls].w;
		if (k <= lsz) return kth(t[rx].ls, t[ry].ls, l, mid, k);
		return kth(t[rx].rs, t[ry].rs, mid + 1, r, k - lsz);
	}
}

ll solve(int n, int m, int w, vint T, vint X, vint Y, vint A, vint B, vint C, vint L, vint R) {
	for (int i = 0; i < m; i++) tr[i] = {X[i], Y[i], A[i], B[i], C[i]};
	for (int i = 0; i < w; i++) mr[i] = {L[i], R[i]};
	sort(mr, mr + w, cmpr);
	for (int i = 0; i < w; i++) mr[i].rk = i, ml[i] = mr[i];
	sort(ml, ml + w, cmpl);
	for (int i = 0; i < w; i++) {
		if (i) rt[i] = rt[i - 1];
		PST::upd(rt[i], rt[i], 0, w - 1, ml[i].rk);
	}
	sort(tr, tr + m);
	memset(f, -1, sizeof(f)), memset(tl, -1, sizeof(tl));
	to[0].emplace_back(m), pre[0].emplace_back(-1), f[m] = 0, tr[m].y = 0, tr[m].b = -1, hd[0] = tl[0] = 0;

	int cnt = 0;

	auto val = [&](int i) -> ll {
		if (cnt) {
			int p = upper_bound(ml, ml + w, Meal{tr[i].b, INF, INF}, cmpl) - ml;
			return f[i] + 1ll * (cnt - (p ? PST::query(rt[p - 1], 0, w - 1, 0, cnt - 1) : 0)) * T[tr[i].y];
		}
		return f[i];
	};

	auto kth = [&](int l, int r, int k) -> int {
		int x = lower_bound(ml, ml + w, Meal{l, -1, -1}, cmpl) - ml, y = upper_bound(ml, ml + w, Meal{r, INF, INF}, cmpl) - ml;
		if (!y || y - x < k) return -1;
		return PST::kth(x ? rt[x - 1] : 0, rt[y - 1], 0, w - 1, k);
	};

	auto calc = [&](int i) -> void {
		int p = tr[i].y, j = to[p][pre[p][id[i]]], rk = kth(tr[j].b + 1, tr[i].b, (f[i] - f[j] + T[p] - 1) / T[p]);
		if (rk != -1) c[rk].emplace_back(i);
	};

	for (int i = 0; i < m; i++) {
		while (cnt < w && mr[cnt].r < tr[i].a) {
			int cur = cnt++;
			for (int x : c[cur]) if (!vis[x]) {
				int p = tr[x].y;
				if (id[x] == hd[p]) continue;
				while (val(to[p][pre[p][id[x]]]) >= val(x)) {
					vis[to[p][pre[p][id[x]]]] = 1;
					if (pre[p][id[x]] == hd[p]) {hd[p] = id[x]; break;}
					pre[p][id[x]] = pre[p][pre[p][id[x]]];
				}
				if (hd[p] != id[x]) calc(x);
			}
		}
		while (!q.empty() && q.top().first <= tr[i].a) {
			int x = q.top().second; q.pop();
			int p = tr[x].y;
			while (hd[p] <= tl[p] && val(x) <= val(to[p][tl[p]])) {
				if (hd[p] == tl[p]) {hd[p] = to[p].size(); break;}
				vis[to[p][tl[p]]] = 1, tl[p] = pre[p][tl[p]];
			}
			id[x] = to[p].size(); to[p].emplace_back(x);
			if (hd[p] > tl[p]) pre[p].emplace_back(-1), hd[p] = tl[p] = id[x];
			else pre[p].emplace_back(tl[p]), tl[p] = id[x], calc(x);
		}
		int p = tr[i].x;
		if (hd[p] <= tl[p]) f[i] = val(to[p][hd[p]]) + tr[i].c, q.emplace(tr[i].b, i);
	}
	cnt = w; ll ans = 9e18;
	for (int i = 0; i < m; i++) if (tr[i].y == n - 1 && f[i] != -1) ans = min(ans, val(i));
 	return ans < 9e18 ? ans : -1;
}

C-show

乱搞。

树的规模肯定越大越好,所以取 \(n = 5000\)

对每一个点 \(i \ge 2\),连边 \(i \to X \bmod (i - 1) + 1\)。得到 \(2499\) 个同余方程,可以解出 \(X\)

也不用 exCRT,朴素 \(\mathcal O(n^2)\) 解就行。

Alice:

#include <bits/stdc++.h>
#include "Alice.h"

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

constexpr int N = 5e3;

vector<pii> Alice() {
    vector<pii> res;
    ll x = setN(N);
    for (int i = 2; i <= N; i++) res.emplace_back(pii(i, x % (i - 1) + 1));
    return res;
}

Bob:

#include <bits/stdc++.h>
#include "Bob.h"

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;
typedef __int128 lll;

ll Bob(vector<pii> v) {
    lll x = 0, p = 1;
    for (auto e : v) {
        int xi = e.first - 1, pi = e.second - 1;
        while (x % pi != xi) x += p;
        p = p * pi / __gcd(p, lll(pi));
        if (p > 1e18) return x;
    }
    return x;
}
posted @ 2024-05-28 10:40  Chy12321  阅读(42)  评论(0编辑  收藏  举报