树状数组二分 学习笔记

首先树状数组大家都很熟悉:

可以进行单点加/区间查 单次复杂度 \(O(\log n)\)

而且我们会发现:在一个位置 \(p\)\(d\) 步后的位置存储的是原数组 \([p+1,p+d]\) 的区间和

(当然了要保证 \(d=2^k,p-1\)\(d\) 的倍数)

看图很好理解:

image

这是进行树状数组二分(额好像叫它倍增更对?)的关键

P6619 [省选联考 2020 A/B 卷] 冰火战士

题目看起来很花哨 其实离散化之后就是求:

\[2\times \max(\min(\sum\limits_{i\in ice,i=1}^k y_i,\sum\limits_{j\in fire,j=k}^n y_j)) \]

每次操作相当于单点修改 询问前缀/后缀和 很容易想到用树状数组维护

那么我们发现:\(\sum\limits_{i\in ice,i=1}^k y_i\) 单调不降 \(\sum\limits_{j\in fire,j=k}^n y_j\) 单调不升

\(f_k=\min(\sum\limits_{i\in ice,i=1}^k y_i,\sum\limits_{j\in fire,j=k}^n y_j)\) 那么它一定先升后降 类似单峰函数

好像可以三分?因为函数可能有连续的几段,而且题目要求取横坐标最大值,做不了。

由于峰顶可能不是整数,那么我们考虑二分:

  • 最大的 \(k\) 满足 \(\sum\limits_{i\in ice,i=1}^k y_i<\sum\limits_{j\in fire,j=k}^n y_j\)
  • 最小的 \(k\) 满足 \(\sum\limits_{i\in ice,i=1}^k y_i\ge\sum\limits_{j\in fire,j=k}^n y_j\) 设此时答案为 \(ans\) (实际上这时 \(k\) 为上一个+1 所以实际上不用二分)
  • 最大的 \(k\) 满足 \(\min(\sum\limits_{i\in ice,i=1}^k y_i,\sum\limits_{j\in fire,j=k}^n y_j)=ans\)

答案即为1.3二分的 \(f_k\) 最大值 \(\times2\)

这样就得到了一个 \(O(n\log^2 n)\) 的做法 但 \(2e6\) 常数再小也跑不过去()

考虑怎么把它变成一个 \(\log\)

发现每次求前缀都要在全算一遍 然而这样肯定会算重

那么我们考虑每次往前跳 \(2^{20},2^{19}...2^0\) 那么直接累加 \(sum\) 这样就相当于加上了跳过去的区间的贡献

那么我们判断此时是否更优 如果是记录跳过去的位置

否则证明答案在左侧 那么之后小步跳即可

这样复杂度就是严格 \((n\log n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline
const int N=2e6+5;
const int M=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
inl int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*f;
}
inl void write(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inl void writei(int x){write(x);putchar(' ');}
inl void writel(int x){write(x);putchar('\n');}
int q,n,m,lsh[N],sumfir;
struct qus{
    int op,t,x,y,k;
}a[N];
struct BIT{
    int c[N];
    inl void add(int x,int y){
        for(;x<=n;x+=x&(-x))c[x]+=y;
    }
    inl int query(int x){
        int ans=0;
        for(;x;x-=x&(-x))ans+=c[x];
        return ans;
    }
}ice,fir;
signed main(){
    freopen("1.in","r",stdin);
    q=read();
    for(int i=1;i<=q;i++){
        a[i].op=read();
        if(a[i].op==1){
            a[i].t=read(),a[i].x=read(),a[i].y=read();
            lsh[++m]=a[i].x;
        }else a[i].k=read();
    }
    sort(lsh+1,lsh+m+1);
    n=unique(lsh+1,lsh+m+1)-lsh-1;
    for(int i=1;i<=q;i++){
        if(a[i].op==1)
            a[i].x=lower_bound(lsh+1,lsh+n+1,a[i].x)-lsh;
    }
    for(int i=1;i<=q;i++){
        if(a[i].op==1){
            if(!a[i].t)ice.add(a[i].x,a[i].y);
            else fir.add(a[i].x+1,a[i].y),sumfir+=a[i].y;
        }else{
            int k=a[i].k;
            if(!a[k].t)ice.add(a[k].x,-a[k].y);
            else fir.add(a[k].x+1,-a[k].y),sumfir-=a[k].y;
        }
        int pos1=0,fx1=0,gx1=sumfir;
        for(int i=20;~i;i--){
            int ip=pos1+(1<<i);
            if(ip>n)continue;
            int ifx=fx1+ice.c[ip],igx=gx1-fir.c[ip];
            if(ifx<igx){
                pos1=ip,fx1=ifx,gx1=igx;
            }
        }
        int ans=min(ice.query(pos1+1),sumfir-fir.query(pos1+1));
        int pos2=0,fx2=0,gx2=sumfir;
        if(pos1>=n){puts("Peace");continue;}
        for(int i=20;~i;i--){
            int ip=pos2+(1<<i);
            if(ip>n)continue;
            int ifx=fx2+ice.c[ip],igx=gx2-fir.c[ip];
            if(ifx<igx||ifx>=igx&&igx==ans){
                pos2=ip,fx2=ifx,gx2=igx;
            }
        }
        if(!max(fx1,gx2)){puts("Peace");continue;}
        if(fx1>gx2)writei(lsh[pos1]),writel(fx1*2);
        else writei(lsh[pos2]),writel(gx2*2);
    }
    return 0;
}
posted @ 2023-11-03 20:00  xiang_xiang  阅读(80)  评论(0编辑  收藏  举报