日常训练2025-1-24

日常训练2025-1-24

699. 掉落的方块

rating:困难

思路(线段树模拟)

模拟一下这个过程发现,需要维护每个小区间已经有的高度,还要方便的查出小区间的最大值以及整个区间的最大值,所以用线段树维护。

代码

struct Info{
int val = 0;
};
Info operator+(const Info &a, const Info &b){
if (a.val > b.val) return a;
return b;
}
struct SegmentTree {
int n;
std::vector<int> tag;
std::vector<Info> info;
SegmentTree(int n_) : n(n_), tag(4 * n), info(4 * n) {}
// 汇总信息
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
//懒更新
void add(int p, int v) {
tag[p] += v;
info[p].val += v;
}
// 把信息发下去
void push(int p) {
add(2 * p, tag[p]);
add(2 * p + 1, tag[p]);
tag[p] = 0;
}
// 查询是左闭右开的
Info query(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return {};
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
push(p);
return query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y);
}
Info query(int x, int y) {
return query(1, 0, n, x, y);
}
void rangeAdd(int p, int l, int r, int x, int y, int v) {
if (l >= y || r <= x) {
return;
}
if (l >= x && r <= y) {
return add(p, v);
}
int m = (l + r) / 2;
push(p);
rangeAdd(2 * p, l, m, x, y, v);
rangeAdd(2 * p + 1, m, r, x, y, v);
pull(p);
}
// 左闭有开
void rangeAdd(int x, int y, int v) {
rangeAdd(1, 0, n, x, y, v);
}
void modify(int p, int l, int r, int x, const Info &v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
push(p);
if (x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int x, const Info &v) {
modify(1, 0, n, x, v);
}
};
class Solution {
public:
vector<int> fallingSquares(vector<vector<int>>& positions) {
int n = positions.size();
std::vector<int> a;
for (auto e : positions) {
a.push_back(e[0]);
a.push_back(e[1]);
}
std::sort(a.begin(), a.end());
a.erase(std::unique(a.begin(), a.end()), a.end());
int len = a.size();
std::vector<int> ans;
SegmentTree stree(len);
for (int i = 0; i < n; i++){
int pl = positions[i][0], pr = pl + positions[i][1];
int l = std::lower_bound(a.begin(), a.end(), pl) - a.begin();
int r = std::lower_bound(a.begin(), a.end(), pr) - a.begin() - 1;
int oldval = stree.query(l, r + 1).val;
int newval = oldval + positions[i][1];
for (int j = l; j <= r; j++){
stree.modify(j, {newval});
}
ans.push_back(stree.query(0, len).val);
}
return ans;
}
};

E 一起走很长的路!

rating:1700

https://ac.nowcoder.com/acm/contest/95334/E

思路(ST表维护最值)

如果从 1 开始推倒,那么每个位置之前的重量之和就是前缀和,记为 f[i1]

如果自身重量大于前面的前缀和,那我们就需要进行操作。

那么我们应该怎么操作呢?

一个比较显然的贪心想法是,将第 1 块多米诺骨牌的重量增加 aif[i1]

那么对于整个数组需要给第 1 块多米诺骨牌增加多少重量呢?显然是数组中 di[2,n])的最大值。

那么现在如果是 [l,r]子数组怎么办呢?

在子数组中应该这样更新: di

这时我对子数组进行一次区间加,再查询最大值就行了,可以直接使用线段树暴力处理。

当然,我们再稍微思考一下可以发现,根本不需要进行区间加,直接查询最大值,然后将最大值加上 fl−1f**l−1 即可,可以使用ST表。

注意,最大值要从 l+1 开始取,因为第 1 块不需要靠前面的推倒。

时间复杂度 O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;
template <typename T>
class ST{
public:
const int n;
vector<vector<T>> st;
ST(int n = 0, vector<T> &a = {}) : n(n){
st = vector(n + 1, vector<T>(22 + 1));
build(n, a);
}
inline T get(const T &x, const T &y){
return max(x, y);
}
void build(int n, vector<T> &a){
for(int i = 1; i <= n; i++){
st[i][0] = a[i];
}
for(int j = 1, t = 2; t <= n; j++, t <<= 1){
for(int i = 1; i <= n; i++){
if(i + t - 1 > n) break;
st[i][j] = get(st[i][j - 1], st[i + (t >> 1)][j - 1]);
}
}
}
inline T find(int l, int r){
int t = log(r - l + 1) / log(2);
return get(st[l][t], st[r - (1 << t) + 1][t]);
}
};
int main(){
int n, q;
cin >> n >> q;
vector f(n + 1, 0ll), d = f;
for(int i = 1; i <= n; i++){
int x;
cin >> x;
f[i] = f[i - 1] + x;
d[i] = x - f[i - 1];
}
ST<long long> st(n, d);
while(q--){
int l, r;
cin >> l >> r;
if(l == r){
cout << 0 << endl;
continue;
}
auto ma = st.find(l + 1, r);
auto ans = max(ma + f[l - 1], 0ll);
cout << ans << endl;
}
}

E. Turtle vs. Rabbit Race: Optimal Trainings

rating:1500

https://codeforces.com/problemset/problem/1933/E

思路:二分+小巧思

容易想到的是,存在一个 r 使得跑的节数超过 r 后反而会减小能力值。所以就是在前缀和数组中二分地找这个 r , 此 r 肯定得到的 sum(l, r) 是越靠近 u 越好(靠近可以从左边靠近,也可以从右边靠近),我们二分时找的是小于等于 u 的最大的 r,但是可能还存在一个 > r 的值更优,这个值只可能在 r + 1 位置,所以判断一下即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 5;
typedef long long ll;
ll a[maxn], pre[maxn];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pre[i] = pre[i - 1] + a[i];
}
int q;
cin >> q;
while (q--) {
int l, target;
cin >> l >> target;
int r;
int left = l, right = n;
while (left + 1 < right) {
int mid = (left + right) / 2;
if (pre[mid] <= target + pre[l - 1]) left = mid;
else right = mid;
}
if (pre[right] <= target + pre[l - 1]) r = right;
else r = left;
if (r == n || target - (pre[r] - pre[l - 1]) < pre[r + 1] - pre[l - 1] - target ) {
cout << r << ' ';
}
else {
cout << r + 1 << ' ';
}
}
cout << endl;
}
int main() {
int T;
cin >> T;
while (T--) solve();
}

时空的交织

rating:1400

https://ac.nowcoder.com/acm/contest/67746/I

思路(小巧思+DP)

我们考虑乘法分配律:(ai+aj)(bx+by)=aibx+aiby+ajbx+ajby​,也就是说,任取一个子矩形,我们相当于是求a数组的一个子数组之和乘上b数组的一个子数组之和。

如果是一个数组求连续子数组最大和,那么是一个非常经典的问题。但需要注意的是,并非最大乘最大就是本题的答案,还有可能是最小乘最小(两个数组均全负数)、最小乘最大(一个数组全负数,另一个数组全正数)等等。所以需要考虑所有的情况。

代码

#include <bits/stdc++.h>
using u64 = unsigned long long;
using i64 = long long;
typedef std::pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const long long LINF = 1e18;
void solve(){
int n, m;
std::cin >> n >> m;
i64 mx1 = -LINF, mn1 = LINF, mx2 = -LINF, mn2 = LINF, mn = 0, mx = 0;
std::vector<i64> a(n+1), b(m+1);
for (int i = 1; i <= n; i++){
std::cin >> a[i]; a[i] += a[i-1];
mx1 = std::max(mx1, a[i] - mn);
mn = std::min(mn, a[i]);
mn1 = std::min(mn1, a[i] - mx);
mx = std::max(a[i], mx);
}
mn = 0, mx = 0;
for (int i = 1; i <= m; i++){
std::cin >> b[i]; b[i] += b[i-1];
mx2 = std::max(mx2, b[i] - mn);
mn = std::min(mn, b[i]);
mn2 = std::min(mn2, b[i] - mx);
mx = std::max(b[i], mx);
}
i64 ans = -LINF;
for (auto x : {mx1, mn1}){
for (auto y : {mx2, mn2}){
ans = std::max(1LL * x * y, ans);
}
}
std::cout << ans << '\n';
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(15);
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}

本文作者:califeee

本文链接:https://www.cnblogs.com/califeee/p/18689635

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   califeee  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.