JOI 2020 Final 火灾

Posted iefnah06

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JOI 2020 Final 火灾相关的知识,希望对你有一定的参考价值。

URL

https://loj.ac/problem/3256

简要题意

有一个长度为n的数字序列s[1...n]

再接下来的n个时刻,按照从后往前的顺序,每个s[i]都会和s[i-1]取max(在n+1个时刻后序列肯定不会改变了,每个位置都是前缀最大值)

多次询问(t,l,r),表示第t个时刻[l,r]内数字的和

n,q 200000

解法

可以看看官方题解 https://www.ioi-jp.org/joi/2019/2020-ho/2020-ho-t5-review.pdf 的图

考虑每一个位置的数字在(n+1)*n的矩阵中的连通块(如果在取max时两个数相等,我们认为新的max值来自后面的那个)。

对每个数字用单调栈算出:

  1. pl[i]:左边第一个比它大的位置,不存在的话设为-1
  2. pr[i]:右边第一个不比它小的位置

按照pl[i]分两种情况:

  1. 左边的每个数都不超过它:覆盖范围是以pr[i]-1为右边界的矩形,扣去一个以pr[i]-1为有边界的直角三角形。
  2. 左边存在大于它的:覆盖范围是一个平行四边形,可以差分成三个直角三角形。

然后离线+树状数组即可。

注意三角形得拆成一个后缀直角梯形,扣掉一个后缀矩形。具体实现时可以把梯形和矩形的贡献分开来统计,统计梯形的贡献时每次把数轴整体右移一位。

实现

#include <bits/stdc++.h>
using namespace std;

#define rng(i,a,b) for(int i=int(a);i<int(b);i++)
#define rep(i,b) rng(i,0,b)
#define gnr(i,a,b) for(int i=int(b)-1;i>=int(a);i--)
#define per(i,b) gnr(i,0,b)
#define pb push_back
#define eb emplace_back
#define bg begin()
#define ed end()
#define all(x) (x).bg,(x).ed
#define si(x) int((x).size())
#define mp make_pair
#define a first
#define b second

#ifdef LOCAL
#define dmp(x) cerr<<__LINE__<<" "<<#x<<" "<<x<<endl
#else
#define dmp(x) void(0)
#endif

template<class t,class u> void chmax(t&a,u b){if(a<b)a=b;}
template<class t,class u> void chmin(t&a,u b){if(b<a)a=b;}

template<class t> using vc=vector<t>;
template<class t> using vvc=vc<vc<t>>;

using ll=long long;
using uint=unsigned int;
using pi=pair<int,int>;
using vi=vc<int>;

int topbit(signed t){
    return t==0?-1:31-__builtin_clz(t);
}
int topbit(ll t){
    return t==0?-1:63-__builtin_clzll(t);
}

template<class t>
struct BIT{
    vc<t> buf;
    int s;
    BIT(int s_=0){init(s_);};
    void init(int s_){buf.assign(s=s_,0);}
    void add(int i,t v){
        for(;i<s;i+=(i+1)&(-i-1))
            buf[i]+=v;
    }
    t get(int i){
        t res=0;
        for(;i>=0;i-=(i+1)&(-i-1))
            res+=buf[i];
        return res;
    }
    t sum(int b,int e){
        return get(e-1)-get(b-1);
    }
    /*int kth(int k){
        int res=0;
        for(int i=topbit(s);i>=0;i--){
            int w=res+(1<<i);
            if(w<=s&&buf[w-1]<=k){
                k-=buf[w-1];
                res=w;
            }
        }
        return res;
    }*/
};

template<class t>
struct X{
    BIT<t> a,b;
    int s;
    X(int s_){
        s=s_;
        a.init(s);
        b.init(s);
    }
    void add(int i,t v){
        a.add(i,v);
        b.add(i,-i*v);
    }
    void add(int l,int r,t v){
        add(l,v);
        add(r,-v);
    }
    t get(int i){
        return i*a.get(i-1)+b.get(i-1);
    }
    t get(int l,int r){
        return get(r)-get(l);
    }
};

struct trape{
    int i,v;
};
struct rect{
    int l,r,v;
};
struct query{
    int l,r,i;
};

const int nmax=2.1e5;
const int qmax=2.1e5;
int n,q;
int s[nmax];
int pl[nmax],pr[nmax];

vc<trape> etrape[nmax];
vc<rect> erect[nmax];

void addtrape(int i,int h,int v){
    etrape[0].pb(trape{i,v});
    etrape[h].pb(trape{i+h,-v});
}

void addrect(int l,int r,int h,int v){
    if(l==r)return;
    erect[0].pb(rect{l,r,v});
    erect[h].pb(rect{l,r,-v});
}

void addtri(int l,int r,int v){
    if(l==r)return;
    addtrape(l,r-l,v);
    addrect(r,n,r-l,-v);
}

vc<query> qs[nmax];
ll res[qmax];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin>>n>>q;
    rep(i,n)cin>>s[i];

    {
        vi st;
        rep(i,n){
            while(si(st)&&s[st.back()]<=s[i]){
                pr[st.back()]=i;
                st.pop_back();
            }
            if(si(st))pl[i]=st.back();
            else pl[i]=-1;
            st.pb(i);
        }
        while(si(st)){
            pr[st.back()]=n;
            st.pop_back();
        }
    }

    rep(i,n){
        if(pl[i]==-1){
            addrect(i,pr[i],n+1,s[i]);
            addtri(i+1,pr[i],-s[i]);
        }else{
            addtri(pl[i]+1,pr[i],s[i]);
            addtri(pl[i]+1,i,-s[i]);
            addtri(i+1,pr[i],-s[i]);
        }
    }

    X<ll> xtrape(2*n+10);
    X<ll> xrect(n+10);

    rep(i,q){
        int t,l,r;cin>>t>>l>>r;l--;
        qs[t].pb(query{l,r,i});
    }

    int shift=n+5;
    rep(t,n+1){
        for(auto et:etrape[t])
            xtrape.add(et.i+shift,et.v);
        for(auto er:erect[t])
            xrect.add(er.l,er.r,er.v);
        auto get=[&](int l,int r)->ll{
            return xrect.get(l,r)+xtrape.get(l+shift,r+shift);
        };
        for(auto dt:qs[t])
            res[dt.i]=get(dt.l,dt.r);
        shift--;
    }

    rep(i,q)
        cout<<res[i]<<'
';
}

以上是关于JOI 2020 Final 火灾的主要内容,如果未能解决你的问题,请参考以下文章

loj#2334 「JOI 2017 Final」JOIOI 王国

年轮蛋糕JOI2014Final

#3468. 「JOI 2021 Final」有趣的家庭菜园 4(双指针&差分)

[JOI 2015 Final]分蛋糕 2

2018.10.1「JOI 2014 Final」年轮蛋糕

[JOI 2014 Final]IOI 馒头