首页 > 其他 > 详细

JOI2020FinalE 火事

时间:2020-03-20 21:32:00      阅读:71      评论:0      收藏:0      [点我收藏+]

Link
首先把询问差分,让询问区间变成一段前缀,然后把询问离线并按时间升序排序。
\(L_i=\max\{j|j<i\wedge a_j>a_i\},R_i=\min\{j|j>i\wedge a_j>a_i\}\)\(a_{t,i}\)表示在\(t\)时刻\(i\)位置的数。
我们考虑以序列下标作为横坐标,时间作为纵坐标建出直角坐标系,然后在这个直角坐标系上表示数的增加。
\(i\)会在\(j-i\)时刻覆盖\(j\),其中\(j\in(i,R_i)\)。但是直接这样处理无法计算增量,因为在\(i\)覆盖\(j\)之前可能会有\(k\in(i,j)\)覆盖了\(j\)
简单推理可以发现,所有覆盖都可以表示成以下形式:\(L_i\)覆盖了\([i,R_i)\)这一段区间,让这一段区间加上了\(a_{L_i}-a_i\),即\(\{(x,y)|x\in[i,R_i)\wedge y\ge x-L_i\}\)这一直角梯形内的点的点权加上\(a_{L_i}-a_i\)
那么对于询问\((t,p)\),答案就是\(\{(x,t)|x\le p\}\)的点权和加上初始的\(\sum\limits_{i=1}^pa_i\),后一部分可以利用前缀和统计。
然后我们考虑将直角梯形转化为两个直角三角形的差,这样我们的修改就变成了\(\{(x,y)|x\ge a\wedge y\ge x-b\}\)这一直角三角形内的点的点权加上\(c\)。不妨用\((a,b,c)\)来表示这样的一组修改。
分析可得修改\((a,b,c)\)对询问\((t,p)\)的贡献(保证\(t\ge a\))为\(c(\min(p,t+b)-\min(p,a+b-1))=c(\min(p,a+b-1)+\min(p-t,b)+t)\)
不难发现\(\min\)中的两边分别之和修改/询问有关,那么用BIT维护即可。

#include<cstdio>
#include<cctype>
#include<vector>
#include<utility>
#include<algorithm>
#define fi first
#define se second
using i64=long long;
using pi=std::pair<int,int>;
const int N=200007;
int n,q,top,stk[N],a[N],L[N],R[N];i64 sum[N],ans[N];std::vector<pi>vec[N],query[N];
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
struct BIT
{
    i64 t[N+N];
    void add(int p,i64 v){for(;p<=n+n;p+=p&-p)t[p]+=v;}
    i64 ask(int p){i64 s=0;for(;p;p^=p&-p)s+=t[p];return s;}
};
struct node
{
    BIT t1,t2;
    void add(int p,i64 v){p+=n,t1.add(1,v),t1.add(p+1,-v),t2.add(p+1,p*v);}
    i64 ask(int p){return p+=n,t1.ask(p)*p+t2.ask(p);}
}t1,t2;
int main()
{
    n=read(),q=read();
    for(int i=1;i<=n;++i) sum[i]=sum[i-1]+(a[i]=read());
    for(int i=1;i<=n;L[i]=stk[top],stk[++top]=i++) while(top&&a[i]>=a[stk[top]]) R[stk[top--]]=i;
    for(int i=1;i<=n;++i)
    if(L[i])
    {
        vec[i-L[i]].push_back({i,a[L[i]]-a[i]});
        if(R[i]) vec[R[i]-L[i]].push_back({R[i],a[i]-a[L[i]]});
    }
    for(int i=1,t,l,r;i<=q;++i) t=read(),l=read(),r=read(),ans[i]=sum[r]-sum[l-1],query[t].push_back({r,i}),query[t].push_back({l-1,-i});
    for(int i=1;i<=n;++i)
    {
    for(auto x:vec[i]) t1.add(x.fi-i,x.se),t2.add(x.fi-1,x.se);
    for(auto x:query[i]) x.se>0? ans[x.se]+=t1.ask(x.fi-i)-t2.ask(x.fi):ans[-x.se]-=t1.ask(x.fi-i)-t2.ask(x.fi);
    }
    for(int i=1;i<=q;++i) printf("%lld\n",ans[i]);
}

JOI2020FinalE 火事

原文:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12534625.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!