卷王之王

题意:
给定数组a[n],然后m个操作,每次操作给出一个数x,数组中小于等于x的数都要加上x。最后输出所有的a[i]。
两种解法,一种线段树,另一种是优先队列加一些神奇的优化。
线段树:
维护区间的最小值与最大值,如果最大值小于等于x,则直接修改区间,如果最小值大于x,则直接返回。
最后输出的是每个父亲节点的值。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int a[N], maxx[N * 4], minn[N * 4], tag[N * 4];
int x;
void build(int now, int l, int r)
{
    if (l >= r) {
        maxx[now] = minn[now] = a[l];
        return;
    }
    int mid = (l + r) / 2;
    int chl = 2 * now, chr = 2 * now + 1;
    build(chl, l, mid);
    build(chr, mid + 1, r);
    maxx[now] = max(maxx[chl], maxx[chr]);
    minn[now] = min(minn[chl], minn[chr]);
}
void down(int now, int l, int r)
{
    int chl = 2 * now, chr = 2 * now + 1;
    tag[chl] += tag[now] ,tag[chr] += tag[now];
    maxx[chl] += tag[now], maxx[chr] += tag[now];
    minn[chl] += tag[now], minn[chr] += tag[now];
    tag[now] = 0;
}
void xg(int now, int l, int r)
{
    if (maxx[now] <= x) {
        maxx[now] += x;
        minn[now] += x;
        tag[now] += x;
        return;
    }
    else if (minn[now] > x) return; 
    //else if (l >= r) return;
    else {
        down(now, l, r);
        int mid = (l + r) / 2;
        int chl = 2 * now, chr = 2 * now + 1;
        xg(chl, l, mid);
        xg(chr, mid + 1, r);
        maxx[now] = max(maxx[chl], maxx[chr]);
        minn[now] = min(minn[chl], minn[chr]);
    }
}
void print(int now, int l, int r)
{
    if (l >= r) {
        printf("%lld ", maxx[now]);
        return;
    }
    down(now, l, r);
    int mid = (l + r) / 2;
    int chl = 2 * now, chr = 2 * now + 1;
    print(chl, l, mid);
    print(chr, mid + 1, r);
}
signed main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        scanf("%lld", &x);
        xg(1, 1, n);
    }
    print(1, 1, n);
    return 0;
}

优先队列:
如果所有元素为1。
x=1,变成2。
x=2,变成4。
x=4,变成8。
x=8,变成16。
所以数组里的数最多被加log级别的次数,可以使用看似暴力实则不暴力的优先队列。
这里的优化是,如果x=0,那么加了相当于没加,而且还要访问一遍所有的a[i],所以当x=0时要continue。还有一个优化是,pop优先队列的元素后,先不要急着马上push新元素进去,将新元素暂时缓存一个vecotr里,等pop完了再将vector的元素push进去。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int b[N];
struct node {
    int val, pos;
    bool operator < (const node &k) const {
        if (val != k.val) return val > k.val;
        return pos < k.pos;
    }
} a;
priority_queue<node> q;
vector<node> v;
signed main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int x;
        scanf("%lld", &x);
        q.push({x, i});
    }
    for (int i = 1; i <= m; i++) {
        int x;
        scanf("%lld", &x);
        if (x == 0) continue;
        while(!q.empty() && q.top().val <= x) {
            v.push_back({q.top().val + x, q.top().pos});
            q.pop();
        }
        int len = v.size();
        for (int j = 0; j < len; j++) q.push(v[j]);
        v.clear();
    }
    while(!q.empty()) {
        b[q.top().pos] = q.top().val;
        q.pop();
    }
    for (int i = 1; i <= n; i++) printf("%lld ", b[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值