Luogu 3246 序列

  • 考虑莫队,不算特别优秀,但足以通过此题.
  • 用莫队做,先考虑在当前区间右边加入一个数对答案的影响,其他三种情况同理.
  • 若加入新数的区间为 \([L,R]\) ,那么加的贡献就是 \([L,R],[L+1,R]\dots [R,R]\) 这些区间最小值之和.
  • 用单调栈预处理出每个数 \(a_i\) 左边第一个比它小的数的位置 \(sl\) ,那么它被记作最小值的区间就是 \([sl+1,R],[sl+2,R]\dots[i,R]\) ,被算了 \(i-sl\) 次.那么就这样一个个往前面跳,类似于树的结构.

  • 这个东西显然可以在算完 \(sl\) 后立刻求出,算一下前缀和,那么每次查询也是 \(O(1)\) 的.
  • 注意到最前面那个元素会跳出去,被算的次数不是 \(i-sl\) ,而是 \(i-L+1\),需要单独算.按照定义,它显然是 \([L,R]\) 这个区间内的最小值,用 \(ST\) 表问一下位置,大小就可以了.

写这个题又复习了一遍莫队...询问排序第一关键字是左端点的块,第二关键字是右端点...另外,那四个 \(while\) 移动端点的顺序不能乱写...不然会出现 \(L>R\) 的尴尬情况..如果样例没测出来这题可就爆零了...

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int MAXN=1e5+10;
struct query
{
int l,r,bel,id;
bool operator < (const query &rhs) const
{
if(bel!=rhs.bel)
return bel<rhs.bel;
if(r!=rhs.r)
return r<rhs.r;
return l<rhs.l;
}
} q[MAXN];
int a[MAXN];
int st[MAXN][18],stp[MAXN][18];
int n,Q;
int sl[MAXN],sr[MAXN];
int stk[MAXN],stkpos[MAXN],tp;
ll suml[MAXN],sumr[MAXN];
ll res,ans[MAXN];
int mi,pos;
inline void st_init()
{
for(int i=1;i<=n;++i)
st[i][0]=a[i],stp[i][0]=i;
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
{
if(st[i][j-1]<st[i+(1<<(j-1))][j-1])
st[i][j]=st[i][j-1],stp[i][j]=stp[i][j-1];
else
st[i][j]=st[i+(1<<(j-1))][j-1],stp[i][j]=stp[i+(1<<(j-1))][j-1];
}
}
void query(int l,int r)
{
int k=log(r-l+1)/log(2.0);
if(st[l][k]<st[r-(1<<k)+1][k])
mi=st[l][k],pos=stp[l][k];
else
mi=st[r-(1<<k)+1][k],pos=stp[r-(1<<k)+1][k];
}
void addl(int L,int R)
{
query(L,R);
ll delta=1LL*(R-pos+1)*mi;
delta+=sumr[L]-sumr[pos];
res+=delta;
}
void reml(int L,int R)
{
query(L,R);
ll delta=1LL*(R-pos+1)*mi;
delta+=sumr[L]-sumr[pos];
res-=delta;
}
void addr(int L,int R)
{
query(L,R);
ll delta=1LL*(pos-L+1)*mi;
delta+=suml[R]-suml[pos];
res+=delta;
}
void remr(int L,int R)
{
query(L,R);
ll delta=1LL*(pos-L+1)*mi;
delta+=suml[R]-suml[pos];
res-=delta;
}
void init()
{
st_init();
stk[++tp]=0;
stkpos[tp]=0;
for(int i=1; i<=n; ++i)
{
while(tp)
{
if(a[i]<=stk[tp])
--tp;
else
break;
}
sl[i]=stkpos[tp];
stk[++tp]=a[i];
stkpos[tp]=i;
suml[i]=suml[sl[i]]+1LL*(i-sl[i])*a[i];
}
tp=0;
stk[++tp]=0;
stkpos[tp]=n+1;
for(int i=n; i>=1; --i)
{
while(tp)
{
if(a[i]<=stk[tp])
--tp;
else
break;
}
sr[i]=stkpos[tp];
stk[++tp]=a[i];
stkpos[tp]=i;
sumr[i]=sumr[sr[i]]+1LL*(sr[i]-i)*a[i];
}
}
int main()
{
n=read(),Q=read();
int Blocksize=sqrt(Q);
for(int i=1; i<=n; ++i)
a[i]=read();
for(int i=1; i<=Q; ++i)
{
q[i].l=read();
q[i].r=read();
q[i].bel=q[i].l/Blocksize;
q[i].id=i;
}
sort(q+1,q+1+Q);
init();
int L=1,R=0;
for(int i=1; i<=Q; ++i)
{
int l=q[i].l,r=q[i].r;
while(R<r)
addr(L,++R);
while(L<l)
reml(L++,R);
while(L>l)
addl(--L,R);
while(R>r)
remr(L,R--);
ans[q[i].id]=res;
}
for(int i=1; i<=Q; ++i)
printf("%lld\n",ans[i]);
return 0;
}

Luogu 3246 序列的更多相关文章

  1. [Luogu] 维护序列

    https://www.luogu.org/problemnew/show/P2023 线段树双懒标记下放 #include <bits/stdc++.h> using namespace ...

  2. luogu P3411 序列变换

    链接 P3411 序列变换 如果要最小化答案,那么就最大化不移动的数. 那么不移动的子序列一定是最后答案的一段连续区间,而移动的数我们是一定有办法把他们还原的. 设\(f_{i}\)表示\(i\)点的 ...

  3. luogu P4146 序列终结者

    嘟嘟嘟 这是一道splay基础题. 最坑的一点是,因为有些节点可能没有左儿子或右儿子,所以必须把t[0].Max赋成-INF! 因为这个调了半天,看来回头复习复习splay是对的-- #include ...

  4. luogu 1631 序列合并

    priority_queue的使用,注意 a[1]+b[1],a[1]+b[2],a[1]+b[3],a[1]+b[4].......a[1]+b[n] a[2]+b[1]......... .. a ...

  5. luogu 3246 莫队+RMQ+单调栈

    hnoi 2016 标签:题解 莫队 考虑左端点左移以及右端点右移产生的贡献 这样就可以由 \([l, r]\) 转移到另外的 \(4\) 个区间 \(f_{l, r}\) 表示右端点在 \(r\), ...

  6. Luogu P2572 序列操作

    (是道线段树好题√) 题目链接 题外话:这道题我也不知道卡了自己多少天,从初赛之前就开始做,一直到现在才a掉(时间跨度得有将近十天了吧?) 线段树,嗯,好像很简单的样子. 但事实上因为自己太菜了,卡了 ...

  7. Luogu P1631 序列合并

    题目 开一个堆,先把所有\(a[i]+b[1]\)压进优先队列. 然后每次把最小的取出来,把对应的\(a[i]\)的下一个\(b[j]\)拿出来加进去. #include<bits/stdc++ ...

  8. [luogu P3648] [APIO2014]序列分割

    [luogu P3648] [APIO2014]序列分割 题目描述 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序 ...

  9. [Luogu 2023] AHOI2009 维护序列

    [Luogu 2023] AHOI2009 维护序列 恕我冒昧这和线段树模板二有个琴梨区别? #include <cstdio> int n,m; long long p; class S ...

随机推荐

  1. 使用R的数据库查询

    JS 很多方法可以用R查询数据.这篇文章展示了三种最常见的方法: 运用 DBI 使用dplyr语法 使用R note book 背景 最近的一些软件包改进可以更轻松地将数据库与R一起使用.下面的查询示 ...

  2. 什么是分布式锁?Redis实现分布式锁详解

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天继续由陈睿|mikeche ...

  3. php5.4 的 arm 交叉编译

    ./configure --prefix=/h1root/usr/php --host=arm-linux --enable-libxml --with-mysql=mysqlnd --with-my ...

  4. 如何将新项目添加到github仓库中?只需简单几步~即可实现

    问题描述:新建了一个项目,如何将其设置为git项目?如何关联到github上的仓库? 只需简单几步,但前提是需要已经安装好了git,并且有github账户 本文使用IntelliJ IDEA 其他编辑 ...

  5. js setInterval不能访问外网

    今天调用js setInterval,发现不能访问外网,或者说不能访问本身域名以外的其他域名..不知道什么原因,老是弹出: 网络延时,请稍后再试! setInterval(function(){ va ...

  6. 雷林鹏分享:C# 异常处理

    C# 异常处理 异常是在程序执行期间出现的问题.C# 中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零. 异常提供了一种把程序控制权从某个部分转移到另一个部分的方式.C# 异常处理时建立 ...

  7. codeforces 1042d//Petya and Array// Codeforces Round #510 (Div. 2)

    题意:给出一个数组,求其中和小于t的区间数. 先计算前缀和数组sum[i].对当前的sum[i],查询树状数组中有几个比(sum[i]-t)大的数,那么用sum[i]减它就是一个合法区间.再将当前的s ...

  8. java final修饰变量时的一种情况

    有如下一种场景. 1.在文件PaymentConfig.java中存在如下变量public static final desc="描述" 2.类Test.java中使用了desc变 ...

  9. 56. Merge Intervals 57. Insert Interval *HARD*

    1. Merge Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[ ...

  10. OC 构造方法(对象初始化)

    一.构造方法 (一)构造方法的调用 完整的创建一个可用的对象:Person *p=[Person new]; New方法的内部会分别调用两个方法来完成2件事情,1)使用alloc方法来分配存储空间(返 ...