E - Least Elements(树状数组+二分 / set)

文章介绍了两种方法来解决给定长度为N的序列中,每个长度为M的子串内前K小数的和的问题。第一种方法使用了离散化和树状数组,第二种方法通过维护两个集合(in和out)进行实时计算。两种方法都涉及到滑动窗口的概念,并且需要对数据进行有效的排序和查询操作。
摘要由CSDN通过智能技术生成

给定长度为N的序列,对于从前往后每个长度为M的子串求出字串中前K小数的和

解法一

先离散化数据,然后将滑动窗口内的数字个数插入树状数组,数字的值的和插入另一个树状数组,

每次对窗口内的元素查询,二分枚举第一个树状数组前 mid个位置是否存在k个数,查询到包含有k个数的位置后,直接查询另一个树状数组的该位置之前的数字的和即可。要注意,我们不一定能找到一个位置恰好在它之前有k个数,所以要特殊处理下当前有效位置包含的数小于k,下一个有效位置包含的数大于k的情况。详见代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define sz(x) x.size()
#define lbt(x) (x)&(-x)
#define rep(i,n) for(int i=0;i<n;i++)

using namespace std;

typedef pair<int, int> PII;
typedef double db;
const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f;
const db EPS = 1e-9;
int n, m, k;
int t[2][N];
vector<int>v, b;
map<int, int>id;
void add(int u, int i, int x) {
    while (i <= n) {
        t[u][i] += x;
        i += lbt(i);
    }
}

int ask(int u, int i) {
    int res = 0;
    while (i > 0) {
        res += t[u][i];
        i -= lbt(i);
    }
    return res;
}

int query() {
    int l = 1, r = n;
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (ask(0, mid) <= k)l = mid;
        else r = mid - 1;
    }
    if (ask(0, l) == k)return ask(1, l);
    return ask(1, l) + (k - ask(0, l)) * b[l];
}


void solve() {
    cin >> n >> m >> k;
    v.resize(n + 1);
    v[0] = 2e9 + 10;
    for (int i = 1;i <= n;i++)cin >> v[i];
    b = v;
    sort(b.begin(), b.end());
    b.erase(unique(b.begin(), b.end()), b.end());
    for (int i = 0;i < b.size();i++)id[b[i]] = i + 1;
    for (int i = 1;i <= n;i++) {
        add(0, id[v[i]], 1);
        add(1, id[v[i]], v[i]);
        if (i >= m) {
            if (i > m) {
                add(0, id[v[i - m]], -1);
                add(1, id[v[i - m]], -v[i - m]);
            }
            cout << query() << " ";
        }
    }
}
signed main() {
    cin.tie(0)->sync_with_stdio(0);
    cout.tie(0);
    int _ = 1;
    // cin >> _ ;
    while (_--)
        solve();
    return 0;
}

解法二

用两个set维护下整个窗口,前k小的在第一个set(in),后面的部分在另一个se(out)t中,维护过程保证in.size()<k,细节很多,模拟时逻辑一定要清晰。

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define sz(x) x.size()
#define lbt(x) (x)&(-x)
#define rep(i,n) for(int i=0;i<n;i++)

using namespace std;

typedef pair<int, int> PII;
typedef double db;
const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f;
const db EPS = 1e-9;
int a[N];
void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    int res = 0;
    multiset<int>in, out;
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        if (i <= m) {
            res += a[i];
            in.insert(a[i]);
            if (in.size() > k) {
                res -= *in.rbegin();
                out.insert(*in.rbegin());
                in.erase(in.find(*in.rbegin()));
            }
        }
        else {
            int w = a[i - m];
            in.insert(a[i]);
            res += a[i];
            res -= *in.rbegin();
            out.insert(*in.rbegin());
            in.erase(in.find(*in.rbegin()));
            if (w <= *in.rbegin()) {
                res -= w;
                res += *out.begin();
                in.insert(*out.begin());
                out.erase(out.find(*out.begin()));
                in.erase(in.find(w));
            }
            else out.erase(out.find(w));
        }
        if (i >= m)cout << res << " ";
    }
}

signed main() {
    cin.tie(0)->sync_with_stdio(0);
    cout.tie(0);
    int _ = 1;
    // cin >> _ ;
    while (_--)
        solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你想在 uni-forms 表单中校验多个字段,可以使用一个数组来指定这些字段。具体来说,你可以在 `uni-forms` 组件中使用 `:rules` 属性来指定表单校验规则,并将校验规则数组传递给该属性。 例如,如果你想校验一个包含名字、邮箱和密码字段的表单,可以使用以下代码: ```html <uni-forms :rules="formRules"> <uni-form-item label="Name" prop="name"> <uni-input v-model="form.name"></uni-input> </uni-form-item> <uni-form-item label="Email" prop="email"> <uni-input v-model="form.email"></uni-input> </uni-form-item> <uni-form-item label="Password" prop="password"> <uni-input type="password" v-model="form.password"></uni-input> </uni-form-item> </uni-forms> ``` 在这里,我们指定了一个包含三个元素的校验规则数组 `formRules`,每个元素对应一个字段。例如,对于名字字段,我们指定了一个 `required` 规则,对于邮箱字段,我们指定了一个 `required` 和 `email` 规则,对于密码字段,我们指定了一个 `required` 和 `minLength` 规则。 ```javascript data() { return { form: { name: '', email: '', password: '' }, formRules: [ { required: true, message: 'Please enter your name', trigger: 'blur' }, { required: true, message: 'Please enter your email', trigger: 'blur' }, { required: true, min: 6, message: 'Password length must be at least 6 characters', trigger: 'blur' } ] } } ``` 这样,当用户提交表单时,`uni-forms` 组件会自动对这三个字段进行校验,并在校验失败时显示相应的错误信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值