题意
给一个序列 a 1 , a 2 , . . . . a n a_1,a_2,....a_n a1,a2,....an,一个数字m。两种操作
- 输入 l , r , k l, r, k l,r,k,询问 a l , a l + 1 … a r a_l,a_{l+1}\dots a_r al,al+1…ar中,每秒前(r-1+1-k)小的数都+1,问该区间中所有数字都<m的情况最多持续几秒
- x , y x,y x,y,令 a [ x ] = y a[x]=y a[x]=y
一共q次操作,对于每次操作1输出答案
数据范围 1 ≤ n , q ≤ 1 e 5 , 1 ≤ m ≤ 1 e 9 , a i < m 1\le n,q\le 1e5, 1\le m\le 1e9, a_i < m 1≤n,q≤1e5,1≤m≤1e9,ai<m
解题思路
首先,考虑对于问题1,如何解决。
可以二分答案,然后当前二分值为x,则每个数字ai至少需要被制止
x
−
(
m
−
1
−
a
i
)
x-(m-1-a_i)
x−(m−1−ai)次,当
k
x
>
=
∑
i
=
l
r
(
x
−
(
m
−
1
−
a
i
)
)
kx >= \sum_{i=l}^r (x-(m-1-a_i))
kx>=∑i=lr(x−(m−1−ai))的时候说明x秒是可以的。
那么对于静态的这个问题,可以建立主席树,然后区间询问的时候,二分答案之后在log的时间内检查x是否可行,这样是两个log。优化一下,直接在主席树上进行二分,这样复杂度是一个log。
然后因为带修改,所以要树套树,并且理论上树状数组套主席树空间是两个log,会超,要用平衡树套。然后写的就很麻烦。
但是!
在把这题安利给学弟的时候获得了一句金玉良言:
然后你可以发现这题完美的符合整体二分的性质
维护两种操作:
- 在下标为x的地方插入/删除一个权值v
- 询问[l,r,k]的答案
整体二分sol(l, r, L, R)表示e[L]~e[R]的修改和询问在当前二分值[l,r]有贡献,然后检查每个询问的答案区间是在[l,mid]还是[mid+1,r],给操作分类之后递归检查下去
然后这题就用整体二分80行轻松愉快的写完了,时间复杂度
O
(
n
)
l
o
g
2
n
O(n)log^2n
O(n)log2n,空间复杂度
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
using namespace std;
const int maxn = 4e5 + 5;
ll sum[maxn], num[maxn];
void add(ll *a, int i, ll x){assert(i > 0);while(i < maxn) a[i] += x, i += lowbit(i); return;}
ll qry(ll *a, int i){ll res = 0; while(i) res += a[i], i -= lowbit(i); return res;}
struct node{
int op;//1 -1 is add and del , 0 is qry
int l, r, k;//val pos, or l, r
int id;
ll cursum, cursz;
}e[maxn], q1[maxn], q2[maxn];
int tot = 0;
int n, q; ll m;
int a[maxn];
ll ans[maxn];
void sol(ll l, ll r, int L , int R){
if(l > r || L > R) return;
ll mid = (l+r)>>1;
int p1 = 0, p2 = 0;
fors(i,L,R+1){
if(e[i].op){
if(e[i].l <= mid){
add(sum, e[i].r, e[i].op*e[i].l);
add(num, e[i].r, e[i].op);
q1[p1++] = e[i];
}else q2[p2++] = e[i];
}else{
ll cursz = qry(num, e[i].r) - qry(num, e[i].l-1);
ll cursum = qry(sum, e[i].r) - qry(sum, e[i].l-1);
cursz += e[i].cursz; cursum += e[i].cursum;
if( (e[i].k-cursz)*mid+cursum >= 0){
ans[e[i].id] = mid;//a fit ans
e[i].cursum = cursum;
e[i].cursz = cursz;
q2[p2++] = e[i];
}else{
q1[p1++] = e[i];
}
}
}
int o = L;
fors(i,0, p1){
if(q1[i].op){
add(sum, q1[i].r, -q1[i].op*q1[i].l);
add(num, q1[i].r, -q1[i].op);
}
e[o++] = q1[i];
}
if(l == r) return;
fors(i,0,p2) e[o++] = q2[i];
sol(l, mid, L, L+p1-1);
sol(mid+1,r,L+p1,R);
return;
}
int main()
{
scanf("%d%lld%d", &n, &m, &q);
fors(i,1,n+1){
scanf("%d", &a[i]);
e[tot].op = 1;
e[tot].l = m-1-a[i];
e[tot++].r = i;
}
fors(i,1,q+1){
int op; scanf("%d", &op);
if(op == 1){
int l, r, k; scanf("%d%d%d", &l, &r, &k);
e[tot].l = l; e[tot].r = r; e[tot].k = k; e[tot].id = i;
tot++;
}else{
ans[i] = -10086;
int x, y; scanf("%d%d", &x, &y);
e[tot].op = -1; e[tot].l = m-1-a[x]; e[tot].r = x; tot++;
a[x] = y;
e[tot].op = 1; e[tot].l = m-1-a[x]; e[tot].r = x; tot++;
}
}
sol(0, 1e14, 0, tot-1);
fors(i,1,q+1) if(ans[i] != -10086) printf("%lld\n", ans[i]);
}