← 全部文章

AtCoder Grand Contest 040 题解

本文目录(6 节)
  1. ><
  2. Two Contests
  3. Neither AB nor BA
  4. Balance Beam
  5. Prefix Suffix Addition
  6. Two Pieces

AtCoder Grand Contest 040

><

对于每个 <<...<>>...> 必然是两端 00,往中间 +1+1。

头尾特判一下。

const int N = 5e5 + 7;
int n;
char s[N];
ll ans;
vector<pair<char, int>> p;

int main() {
	rds(s, n);
	for (int i = 1, t = 1; i <= n; i++)
		if (i == n || s[i] != s[i+1]) p.pb(mp(s[i], t)), t = 1;
		else ++t;
	if (p.size() == 1u) return print(1ll * p[0].se * (p[0].se + 1) / 2), 0;
	if (p[0].fi == '>') ans += 1ll * p[0].se * (p[0].se + 1) / 2;
	if (p.back().fi == '<') ans += 1ll * p.back().se * (p.back().se + 1) / 2;
	for (ui i = 1; i < p.size(); i++)
		if (p[i].fi == '>') {
			int x = p[i-1].se, y = p[i].se;
			if (x < y) swap(x, y);
			ans += 1ll * x * (x + 1) / 2 + 1ll * y * (y - 1) / 2;
		}
	print(ans);
	return 0;
}

Two Contests

答案只有两种情况:

  • 一个区间 + n−1n-1 个区间。
  • 按 ll 排序之后,某个前缀一个集合,后缀一个集合。
const int N = 1e5 + 7;
int n, ans;
pi a[N];
int l[N], r[N];

int main() {
	rd(n);
	for (int i = 1; i <= n; i++) rd(a[i].fi, a[i].se);
	sort(a + 1, a + n + 1);
	l[0] = r[n+1] = 1e9;
	for (int i = 1; i <= n; i++) l[i] = min(a[i].se, l[i-1]);
	for (int i = n; i; i--) r[i] = min(a[i].se, r[i+1]);
	for (int i = 1; i <= n; i++)
		ans = max(ans, a[i].se - a[i].fi + 1 + max(0, min(l[i-1], r[i+1]) - a[n-(i==n)].fi + 1));
	for (int i = 1; i < n; i++)
		if (a[i].fi <= l[i] && a[n].fi <= r[i+1])
			ans = max(ans, l[i] - a[i].fi + 1 + r[i+1] - a[n].fi + 1);
	print(ans);
	return 0;
}

Neither AB nor BA

将偶数位上的 A 变成 B,B 变成 A,问题等价于不能删 AA 和 BB。

不删 AA 能把 A 删光当且仅当 A 的数量 ≤n2\le \frac n2,B 同理。

于是用总数减去 A 超过 n2\frac n2 的方案数 ×2\times 2 就是答案。

枚举 A 的个数,时间复杂度 O(n)\mathcal O(n)。

const int N = 1e7 + 7;
int n;
modint a[N], now;

int main() {
	rd(n), init(n);
	a[0] = 1;
	for (int i = 1; i <= n; i++) a[i] = a[i-1] * 2;
	for (int i = n / 2 + 1; i <= n; i++) now += binom(n, i) * a[n-i];
	print(((modint)3 ^ n) - 2 * now);
	return 0;
}

Balance Beam

首先对于一个排列,合法的起点范围是一段前缀,所以目标就是相当于最大化前缀的右端点。

考虑借助图象,设 aia_i 之和为 sas_a,bib_i 之和为 sbs_b,建立时间关于位移的折线图,则形成的是两条从 (0,0)(0,0) 开始分别到 (n,sa),(n,sb)(n,s_a),(n,s_b) 的折线 A,BA,B。

将折线 BB 向下平移到恰好与 AA 有交点的最低处,此时 BB 与 xx 轴的交点横坐标就是起点的位置 (x,0)(x,0)。

考虑这样一条折线:从 (x,0)(x,0) 开始沿着 BB 向上走,与 AA 相交之后,沿着 AA 走到 (n,sa)(n,s_a)。

考虑枚举 (x,0)(x,0) 所在的段 kk,则目标就是尽可能最大化 kk 右侧折线的斜率。显然对于段 ii 的斜率为 max⁡(ai,bi)\max(a_i, b_i),因此按照 max⁡(ai,bi)\max(a_i,b_i) 从大到小排序之后在 kk 右侧的折线一定是一个前缀。

最后计算一下 kk 中有多少部分需要放到 xx 的后面,对所有答案取 max⁡\max 即可。

const int N = 1e5 + 7;
int n, k;
pi p[N];
ll s, ss;

int main() {
	rd(n);
	for (int i = 1; i <= n; i++)
		rd(p[i].fi, p[i].se), s += p[i].fi, p[i].fi = max(p[i].fi, p[i].se);
	sort(p + 1, p + n + 1, [&](pi x, pi y) { return x.fi > y.fi; });
	for (int i = 1; i <= n; i++)
		if ((ss += p[i].fi) >= s) {
			k = i;
			break;
		}
	ll P = 1, Q = 1;
	for (int i = 1; i <= n; i++) {
		ll w = s - (ss - p[min(i,k)].fi);
		if (w <= p[i].se && w * Q < P * p[i].se) P = w, Q = p[i].se;
	}
	P = Q - P + (n - k) * Q, Q *= n;
	ll g = __gcd(P, Q);
	print(P / g, Q / g);
	return 0;
}

Prefix Suffix Addition

设通过操作 11 使第 ii 个数增加了 cic_i,有 0≤ci≤ai0 \le c_i \le a_i,操作次数为 ∑i=1n([ci>ci+1]+[ai−ci>ai−1−ci−1])\sum_{i=1}^n ([c_i > c_{i+1}] + [a_i - c_i > a_{i-1} - c_{i-1}])。

考虑 DP,设 fi,jf_{i,j} 表示 ci=jc_i = j 时前 ii 个数的最小操作次数,有转移 fi+1,j′←fi,j+[j>j′]+[ai+1−j′>ai−j]f_{i+1,j^\prime} \leftarrow f_{i,j} + [j > j^\prime] + [a_{i+1} - j^\prime > a_i - j],时间复杂度 O(nw2)\mathcal O(nw^2)。

注意到对于 fi,jf_{i,j} 相同的状态,只用保留 jj 最小的一个。同时若 fi,j′>fi,j+1f_{i,j^\prime} > f_{i,j}+1,那么 fi,j′f_{i,j^\prime} 也没用,于是状态数压缩到了 O(n)\mathcal O(n),时间复杂度 O(n)\mathcal O(n)。

const int N = 2e5 + 7;
int n, a[N];
map<int, int> f[N];

inline void upd(int i, int j, int k) {
	if (f[i].find(j) == f[i].end()) f[i][j] = k;
	else f[i][j] = min(f[i][j], k);
}

int main() {
	rd(n), rda(a, n);
	f[0][0] = 0;
	for (int i = 0; i <= n; i++) {
		int x = 1e9;
		for (pi o : f[i]) x = min(x, o.se);
		auto it = f[i].begin();
		bool ok0 = 0, ok1 = 0;
		while (it != f[i].end())
			if ((it -> se) > x + 1) f[i].erase(it++);
			else if ((it -> se) == x + 1) {
				if (ok1) f[i].erase(it++);
				else ok1 = 1, it++;
			} else {
				if (ok0) f[i].erase(it++);
				else ok0 = 1, it++;
			}
		for (pi o : f[i]) {
			int j = o.fi, x = o.se;
			int p = j, q = a[i+1] - a[i] + j;
			if (p > q) swap(p, q);
			if (p > 0) upd(i + 1, 0, x + 2);
			if (p != q && q > 0 && p <= a[i+1]) upd(i + 1, max(p, 0), x + 1);
			if (q >= 0 && q <= a[i+1]) upd(i + 1, max(q, 0), x);
		}
	}
	print(f[n+1][0]);
	return 0;
}

Two Pieces

咕

标签:DP、组合计数、贪心

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…