题解:[ICPC 2019 Xuzhou R] Yuuki and a problem

· · 题解

题目要你维护一种数据结构,支持:

先不管这些不太聪明的修改和区间查询,我们有个超级弱化版:给你一堆硬币,让你求无法凑出的最小面额。

这个问题的求解很显然了,维护答案变量 ans 表示从 1 到 ans-1 都能凑出来。可以证明 ans 的下界是 1 因为最坏情况下一个硬币不选也可以凑出 0。所以:

然后考虑区间查询和单点修改。瓶颈在于快速求出区间 [l,r] 的 \sum_{i\in[l,r],a_i\leq ans} a_i,即区间内不大于 ans 的数的和,还要支持单点修改。

树状数组套树状数组即可,做完了。很轻松,不需要卡常。

搞笑的是我曾经尝试分块套树然后被卡爆了吃了 3 发罚时。

赛时代码,可读性为滚木:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MX=2e5+5;
const int MV=4e6+5;
int n,q;
int a[MX];
struct Op{
    int t,x,y;
}o[MX];
int vc[MV],vo[MX],vn[MX];
long long fw[MV+MX];
int fo[MX];
void cl(int pos,int val){
    for(int i=pos;i<=n;i+=i&-i){
        vc[vo[i]+vn[i]++]=val;
    }
}
void up(int pos,int val,long long delta){
    for(int i=pos;i<=n;i+=i&-i){
        int p=lower_bound(vc+vo[i],vc+vo[i]+vn[i],val)-(vc+vo[i])+1;
        for(int j=p;j<=vn[i];j+=j&-j){
            fw[fo[i]+j]+=delta;
        }
    }
}
long long qp(int p,long long x){
    long long res=0;
    for(int i=p;i>0;i-=i&-i){
        int idx=upper_bound(vc+vo[i],vc+vo[i]+vn[i],x)-(vc+vo[i]);
        for(int j=idx;j>0;j-=j&-j){
            res+=fw[fo[i]+j];
        }
    }
    return res;
}
long long qr(int l,int r,long long x){
    return qp(r,x)-qp(l-1,x);
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin >>n >>q;
    for(int i=1;i<=n;++i) cin >>a[i];
    for(int i=0;i<q;++i){
        cin >>o[i].t >>o[i].x >>o[i].y;
    }
    int tc[MX];
    for(int i=1;i<=n;i++) tc[i]=0;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j+=j&-j) tc[j]++;
    }
    for(int i=0;i<q;i++){
        if(o[i].t==1){
            int p=o[i].x;
            for(int j=p;j<=n;j+=j&-j) tc[j]++;
        }
    }
    int off=0;
    for(int i=1;i<=n;i++){
        vo[i]=off;
        off+=tc[i];
    }
    for(int i=1;i<=n;i++) vn[i]=0;
    for(int i=1;i<=n;i++) cl(i,a[i]);
    for(int i=0;i<q;i++){
        if(o[i].t==1) cl(o[i].x,o[i].y);
    }
    for(int i=1;i<=n;i++){
        sort(vc+vo[i],vc+vo[i]+vn[i]);
        vn[i]=unique(vc+vo[i],vc+vo[i]+vn[i])-(vc+vo[i]);
    }
    for(int i=1;i<=n;i++) fo[i]=vo[i]+i-1;
    int sz=vo[n]+vn[n]+n;
    for(int i=0;i<sz;i++) fw[i]=0;
    for(int i=1;i<=n;i++) up(i,a[i],a[i]);
    for(int i=0;i<q;i++){
        if(o[i].t==1){
            int p=o[i].x,v=o[i].y;
            if(a[p]!=v){
                up(p,a[p],-a[p]);
                up(p,v,v);
                a[p]=v;
            }
        }else{
            int l=o[i].x,r=o[i].y;
            long long an=1;
            while(1){
                long long s=qr(l,r,an);
                if(s<an){
                    cout <<an <<'\n';
                    break;
                }
                an=s+1;
            }
        }
    }
    return 0;
}