蓝桥杯 灵活区间

题目:

给一个数列 , 求其中所有 \((l , r)\) 对数 , 使得 \(l , r\) 内数变为相同值需要增减数字总和小于 \(m\)

思路:

枚举左端点 \(i\) , \(j\) 指针向右延申 , 不难发现有 \(j\) 单调不减

考虑维护一个对顶堆 , 分别记录大于中位数和小于中位数的数据和 , 然后在更新 \(j\) 时加数据 , 在更新 \(i\) 时删数据即可

这道题调了很久 , 一直有个点 \(TLE\)

后面发现是因为对顶堆查询时用了 \(count\) 实际上判断某元素在哪个节点 只需要检查中位数和它的大小关系就好了

\(count\) 的时间复杂度为 \(O(log(n) + k)\) , 其中 \(k\) 为出现次数

考虑极端数据全都一样 那么此时退化为 \(O(n)\) 所以当然过不了啦

代码:


#include<bits/stdc++.h>
using namespace std;
#define int long long int

inline int read() {
    int ans = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans * f;
}
multiset<int,greater<>>m1;
multiset<int>m2;
const int N = 2e5+10;
int a[N],sum[N];
int n,m;
int suml = 0;
int su = 0;
void kbc() {
    if (m1.size()==m2.size() || m1.size() +1 ==  m2.size())
        return ;
    while (m1.size() < m2.size()) {
        auto u = m2.begin();
        suml += *u;
        m1.insert(*u);
        m2.erase(u);
    }

    while (m1.size() > m2.size()) {
        auto u = m1.begin();
        suml -= *u;
        m2.insert(*u);
        m1.erase(u);
    }

}
void add(int x) {
    if (!m1.size() && !m2.size()) {
        m2.insert(x);
        return ;
    }
    if (x > *m2.begin()) {
        m2.insert(x);
    }
    else {
        suml += x;
        m1.insert(x);
    }
    kbc();
}
void del(int x) {
    if (x <= *m1.begin()) {
        m1.erase(m1.find(x));
        suml -= x;
    }
    else{
        m2.erase(m2.find(x));
    }
    kbc();
}
const int INF = (1<<40)+1;
void make(int l,int r) {
    if (r>n) {
        su = INF;
        return;
    }
    int len = r - l + 1;
    int mid = *m2.begin();
    if (len & 1) {
        su = sum[r] - sum[l-1] - 2* suml - mid;
    }
    else {
        su =  sum[r] - sum[l-1] - 2  * suml;
    }
}
signed main() {
    n =read(),m=read();
    for (int i =1; i<= n; i++) {
        a[i] =read();
        sum[i] = sum[i-1]+ a[i];
    }

    int ans = 0;
    int j = 1;
    add(a[1]);
    for (int i =1 ;i<= n; i++) {
        while (j<=n && su <=m) {
            j++;
            add(a[j]);
            make(i,j);
        }
        ans += j-i;
        if (i==n)
            break;
        del(a[i]);
        make(i+1,j);
    }
    cout<<ans;
    return 0;
}
posted @ 2025-03-18 23:27  Guaninf  阅读(6)  评论(0)    收藏  举报