AtCoder Beginner Contest 407


A - Approximation

点击查看代码
void solve() {
    int a, b;
    std::cin >> a >> b;
    int res = a / b;
    if (a % b > b - a % b) {
    	++ res;
    }
    std::cout << res << "\n";
}

B - P(X or Y)

题意:两次筛子求和大于等于\(x\)差大于等于\(y\)的概率。

暴力枚举

点击查看代码
void solve() {
    int x, y;
    std::cin >> x >> y;
    int sum = 6 * 6, ans = 0;
    for (int i = 1; i <= 6; ++ i) {
    	for (int j = 1; j <= 6; ++ j) {
    		if (i + j >= x || std::abs(i - j) >= y) {
    			++ ans;
    		}
    	}
    }

    std::cout << std::fixed << std::setprecision(12);
    std::cout << (double)ans / sum << "\n";
}

C - Security 2

题意:从空串变成\(s\),每次从后面加入一个\(0\),或者让所有数加一模\(10\)。求最少操作数。

实际是模拟,对于一个递减的子串,可以花费最大值加子串长度的操作使得这一段满足。但这样会改变前面的值,那么假设需要操作\(k\)次,那么前面的先操作\((10 - k) \% 10\)次,这样操作完后前面的也是正确的。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();

    int ans = 0;
    for (int i = 0; i < n; ++ i) {
    	int j = i;
    	while (j + 1 < n && s[j + 1] <= s[j]) {
    		++ j;
    	}

		ans += s[i] - '0';
    	if (i != 0) {
	    	ans += (10 - (s[i] - '0')) % 10;
    	}
    	i = j;
    }

    std::cout << ans + n << "\n";
}

D - Domino Covering XOR

题意:在一个矩阵上放\(1\times2, 2\times 1\)的牌,求没被覆盖的位置的异或和的最大值。

可以把所有可能的覆盖方案枚举出来,然后爆搜,每个方案对应两个点,如果两个点都没被覆盖才能用这个方案,因为大部分方案都和其它有重复的,所以爆搜过程中实际用到的不多。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<i64> a(n * m);
    std::vector<std::pair<int, int>> b;
    i64 sum = 0;
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		i64 x;
    		std::cin >> x;
    		sum ^= x;
    		a[i * m + j] = x;
    		if (j + 1 < m) {
    			b.emplace_back(i * m + j, i * m + j + 1);
    		}

    		if (i + 1 < n) {
    			b.emplace_back(i * m + j, (i + 1) * m + j);
    		}
    	}
    }

    i64 ans = 0;
    std::vector<int> st(n * m);
    auto dfs = [&](auto & self, int u, i64 sum) -> void {
    	if (u == b.size()) {
    		ans = std::max(ans, sum);
    		return;
    	}

    	auto & [x, y] = b[u];
    	if (st[x] || st[y]) {
    		self(self, u + 1, sum);
    		return;
    	}

		self(self, u + 1, sum);
    	st[x] = st[y] = 1;
    	self(self, u + 1, sum ^ a[x] ^ a[y]);
    	st[x] = st[y] = 0;
    };

    dfs(dfs, 0, sum);
    std::cout << ans << "\n";
}

E - Most Valuable Parentheses

题意:一个\(2\times n\)的数组\(a\),你要构造一个合法的括号序列,然后可以得到每个\(s_i=\)'('的\(a_i\)的值。求和最大。

考虑从左到右加入右括号,保证每个前缀右括号的数量小于等于左括号数量,那么如果把\(i\)加入右括号后不满足条件,则需要弹出右括号,因为每个前缀都是合法的,那么不管弹出哪个右括号也不会影响合法性,那么应该从大到小弹,用大根堆模拟就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(2 * n);
    for (int i = 0; i < 2 * n; ++ i) {
    	std::cin >> a[i];
    }

    std::priority_queue<i64> heap;
    i64 sum = 0;
    for (int i = 0; i < 2 * n; ++ i) {
    	heap.push(a[i]);
    	sum += a[i];
    	while (heap.size() > (i + 1) / 2) {
    		sum -= heap.top();
    		heap.pop();
    	}
    }

    i64 ans = std::accumulate(a.begin(), a.end(), 0ll) - sum;

    std::cout << ans << "\n";
}


int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int T = 1; 
	std::cin >> T;
	//scanf("%d", &T);
	while (T -- ) {
		solve();
	} 
	return 0;
}

F - Sums of Sliding Window Maximum

题意:给你一个数组,对于每一个\(k \in [1, n]\),求数组所有长度为\(k\)的子数组的最大值的和。

这种题还是见少了,以前做过这种单调栈的题,但忘了枚举短区间是\(nlogn\)的性质。
用单调栈求出每个位置作为最大值的区间[l_i, r_i],那么可以枚举左端点\(j\),那么\([i - j + 1, r_j - j + 1]\)的答案都要加\(a_i\)。也可以枚举右端点\([j - i + 1, j - l_i + 1]\)的答案都加\(a_i\)。选择枚举短的区间,这个区间长度小于等于\(\frac{r_i - l_1 + 1}{2}\)。均摊下来是\(nlogn\)的。

点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)

template <class Info, class Tag>
struct Node {
	int l, r;
	Info info;
	Tag tag;
};

template <class Info, class Tag>
struct LazySegmentTree {
	std::vector<Node<Info, Tag> > tr;
	LazySegmentTree(int _n) {
		init(_n);
	}

	LazySegmentTree(std::vector<Info> & a) {
		int _n = (int)a.size();
		init(_n, a);
	}

	void init(int _n) {
		tr.assign(_n << 2, {});
		build(0, _n - 1);
	}

	void init(int _n, std::vector<Info> & a) {
		tr.assign(_n << 2, {});
		build(0, _n - 1, a);
	}

	void pushup(int u) {
		tr[u].info = tr[ls].info + tr[rs].info;
	}

	void pushdown(Node<Info, Tag> & u, Tag tag) {
		u.info = u.info + tag;
		u.tag = u.tag + tag;
	}

	void pushdown(int u) {
		if (tr[u].tag.exist()) {
			pushdown(tr[ls], tr[u].tag);
			pushdown(tr[rs], tr[u].tag);
			tr[u].tag.clear();
		}
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, ls); build(mid + 1, r, rs);
		pushup(u);
	}

	void build(int l, int r, std::vector<Info> & a, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			tr[u].info = a[l];
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, a, ls); build(mid + 1, r, a, rs);
		pushup(u);
	}

	void modify(int l, int r, Tag tag, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			pushdown(tr[u], tag);
			return;
		}

		pushdown(u);
		int mid = umid;
		if (l <= mid) {
			modify(l, r, tag, ls);
		}

		if (r > mid) {
			modify(l, r, tag, rs);
		}

		pushup(u);
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		pushdown(u);

		int mid = umid;
		if (r <= mid) {
			return query(l, r, ls);
		}  else if (l > mid) {
			return query(l, r, rs);
		}

		return query(l, r, ls) + query(l, r, rs);
	}

	// int query_first_not_appear(int u = 1) {
	// 	if (tr[u].l == tr[u].r) {
	// 		return tr[u].l;
	// 	}

	// 	pushdown(u);
	// 	int mid = umid;
	// 	if (tr[ls].info.sum != tr[ls].info.len) {
	// 		return query_first_not_appear(ls);
	// 	} else {
	// 		return query_first_not_appear(rs);
	// 	}
	// }
};

struct Info {
	i64 sum, len = 1;
};

struct Tag {
	i64 add;
	bool exist() {	
		return add != 0;
	}

	void clear() {
		add = 0;
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.sum = a.sum + b.sum;
	res.len = a.len + b.len;
	return res;
}

Info operator + (const Info & a, const Tag & b) {
	Info res{};
	res.sum = a.sum + a.len * b.add;
	res.len = a.len;
	return res;
}

Tag operator + (const Tag & a, const Tag & b) {
	Tag res{};
	res.add = a.add + b.add;
	return res;
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    a.push_back(1e9);
    std::vector<int> l(n + 1), r(n + 1);
    std::stack<std::pair<int, int>> stk;
    stk.emplace(1e9 + 1, -1);
    for (int i = 0; i <= n; ++ i) {
    	while (a[i] >= stk.top().first) {
    		r[stk.top().second] = i - 1;
    		stk.pop();
    	}

    	l[i] = stk.top().second + 1;
    	stk.emplace(a[i], i);
    }

    LazySegmentTree<Info, Tag> tr(n + 1);
    for (int i = 0; i < n; ++ i) {
    	if (i - l[i] <= r[i] - i) {
    		for (int j = l[i]; j <= i; ++ j) {
    			int x = i - j + 1, y = r[i] - j + 1;
    			tr.modify(x, y, Tag{a[i]});
    		}
    	} else {
    		for (int j = r[i]; j >= i; -- j) {
    			int x = j - i + 1, y = j - l[i] + 1;
    			tr.modify(x, y, Tag{a[i]});
    		}
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << tr.query(i, i).sum << "\n";
    }
}
posted @ 2025-05-24 21:53  maburb  阅读(229)  评论(0)    收藏  举报