蓝桥杯 灵活区间
题目:
给一个数列 , 求其中所有 \((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;
}

浙公网安备 33010602011771号