2020 CCPC(长春) B-The Tortoise and the Hare (树套树 or 整体二分)

题意

给一个序列 a 1 , a 2 , . . . . a n a_1,a_2,....a_n a1,a2,....an,一个数字m。两种操作

  1. 输入 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+1ar中,每秒前(r-1+1-k)小的数都+1,问该区间中所有数字都<m的情况最多持续几秒
  2. 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 1n,q1e5,1m1e9,ai<m

解题思路

首先,考虑对于问题1,如何解决。
可以二分答案,然后当前二分值为x,则每个数字ai至少需要被制止 x − ( m − 1 − a i ) x-(m-1-a_i) x(m1ai)次,当 k x > = ∑ i = l r ( x − ( m − 1 − a i ) ) kx >= \sum_{i=l}^r (x-(m-1-a_i)) kx>=i=lr(x(m1ai))的时候说明x秒是可以的。
那么对于静态的这个问题,可以建立主席树,然后区间询问的时候,二分答案之后在log的时间内检查x是否可行,这样是两个log。优化一下,直接在主席树上进行二分,这样复杂度是一个log。
然后因为带修改,所以要树套树,并且理论上树状数组套主席树空间是两个log,会超,要用平衡树套。然后写的就很麻烦。

但是!
在把这题安利给学弟的时候获得了一句金玉良言:
在这里插入图片描述
然后你可以发现这题完美的符合整体二分的性质
维护两种操作:

  1. 在下标为x的地方插入/删除一个权值v
  2. 询问[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]);
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值