首页 > 其他分享 >莫队学习笔记(如何处理增量)

莫队学习笔记(如何处理增量)

时间:2023-09-02 20:44:05浏览次数:47  
标签:pre ... return suf fl ll 笔记 增量 莫队

题目传送门:序列

考虑我们已经求出了区间 \([l,r]\) 的答案,现在要求 \([l,r+1]\) 的答案。

很明显增多的子序列有 \((l,r+1),(l+1,r+1)...(r+1,r+1)\)。

考虑求出 \([l,r+1]\) 中的最小值的位置 \(p\) (可以用 \(rmq~O(1)\) 求出),那么 \(a_p\) 的贡献就是 \(a_p\times(p-l+1)\),现在我们只要求出剩下的 \((p+1,r+1),(p+2,r+1)...(r+1,r+1)\) 即可。

考虑 \(DP\),\(f_{l,r}\) 表示区间 \([l,r]\) 的答案,令 \(pre_i\) 表示第一个比 \(i\) 小的数的位置(没有就是 \(0\))。易知:\(f_{l,r}=f_{l,pre_r}+a_r\times(r-pre_r)\),即子序列答案是 \(a_r\) 的和其他的加起来。

考虑优化,因为转移只与 \(r\)有关,所以把 \(l\) 这一维去掉,即剩下 \(f_r=f_{pre_r}+a_r\times(r-pre_r)\)。

那么剩下的子序列与 \(f_{r+1}\) 有什么关系呢?先说结论:\((p+1,r+1)...(r+1,r+1)\)的答案就是 \(f_{r+1}-f_p\)。

证明其正确性:\(f_{r+1}\) 表示的就是 \((1,r+1),(2,r+1)...(r+1,r+1)\) 的答案,而且分成一段段,为\(...,(pre_{pre_{r+1}}+1,pre_{r+1}),(pre_{r+1}+1,r)\),因为 \(a_p\) 是 \([l,r+1]\) 中最小的数,所以这样递归下去,必然出现一个 \(x\) 满足 \(pre_x=p\),而 \(p\) 即 \(p\) 前面的答案就是\(f_p\)(因为 \(p\) 最小,\((p-j,p)\) 这个子序列的答案等价于 \((p-j,r+1)\),相减就是 \((p+1,r+1)...(r+1,r+1)\) 了),所以他是正确的。

那么\([l,r+1]\)的增量与处理一下就可以 \(O(1)\) 求出了,其他同理。

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e5+50;
ll n,Q,a[N],f[N][20],lg[N],mi[20];
ll pre[N],fr[N],suf[N],fl[N];
ll cmp(ll x,ll y)
{
	return a[x]<a[y]?x:y;
}
stack<ll> s;
struct jgt
{
	ll l,r,id;
}q[N];
ll b[N],ans[N],sq;
bool cmp1(jgt t1,jgt t2)
{
	return (b[t1.l]^b[t2.l])?b[t1.l]<b[t2.l]:((b[t1.l]&1)?t1.r<t2.r:t1.r>t2.r);
}
ll qry(ll l,ll r) {ll t=lg[r-l+1]; return cmp(f[l][t],f[r-mi[t]+1][t]);}
ll right(ll l,ll r) {ll p=qry(l,r); return a[p]*(p-l+1)+fr[r]-fr[p];}
ll left(ll l,ll r) {ll p=qry(l,r); return a[p]*(r-p+1)+fl[l]-fl[p];}
int main()
{
	mi[0]=1;
	scanf("%lld %lld",&n,&Q);
	sq=sqrt(n);
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		b[i]=(i-1)/sq+1;
	}
	for(ll i=1;i<=n;i++)
	{
		while(!s.empty()&&a[s.top()]>a[i]) s.pop();
		if(!s.empty()) pre[i]=s.top();
		s.push(i);
	}
	while(!s.empty()) s.pop();
	for(ll i=n;i>=1;i--)
	{
		suf[i]=n+1;
		while(!s.empty()&&a[s.top()]>a[i]) s.pop();
		if(!s.empty()) suf[i]=s.top();
		s.push(i);
	}
	for(ll i=1;i<=n;i++)
		lg[i]=lg[i-1]+((1<<lg[i-1])==i);
	for(ll i=1;i<=n;i++)
		lg[i]--;
	for(ll i=1;i<=lg[n];i++)
		mi[i]=mi[i-1]*2;
	for(ll i=1;i<=n;i++)
		f[i][0]=i;
	for(ll i=1;i<=lg[n];i++)
		for(ll j=1;j<=n-mi[i]+1;j++)
			f[j][i]=cmp(f[j][i-1],f[j+mi[i-1]][i-1]);
	for(ll i=1;i<=n;i++)
		fr[i]=fr[pre[i]]+(i-pre[i])*a[i];
	for(ll i=n;i>=1;i--)
		fl[i]=fl[suf[i]]+(suf[i]-i)*a[i];
	for(ll i=1;i<=Q;i++)
	{
		scanf("%lld %lld",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+Q+1,cmp1);
	ll le=1,ri=0,now=0;
	for(ll i=1;i<=Q;i++)
	{
		while(ri<q[i].r) now+=right(le,++ri);
		while(le>q[i].l) now+=left(--le,ri);
		while(ri>q[i].r) now-=right(le,ri--);
		while(le<q[i].l) now-=left(le++,ri);
		ans[q[i].id]=now;
	}
	for(ll i=1;i<=Q;i++)
	printf("%lld\n",ans[i]);
	return 0;
}

标签:pre,...,return,suf,fl,ll,笔记,增量,莫队
From: https://www.cnblogs.com/pengchujie/p/17674182.html

相关文章

  • 《管理学》阅读笔记(3)
    管理的本质‌‌‌‌管理的本质从某种意义上说是对组织成员在活动中的行为进行协调组织成员的行为能够被有效协调的前提是他们愿意接受这种协调,而且他们的行为具有一定程度的可协调性。管理是对人或对人的行为的管理;‌‌‌‌管理者的主要工作是选择对的人去做对的事情......
  • 学习笔记1-指令级并行
    指令级并行1.概念1.1.指令级并行(ILP)有两种实现方法:(1)依靠硬件来动态发现并实现并行;(2)依靠软件技术在编译时静态发现并行。1.2.数据依赖与冒险数据依赖(三种类型):数据依赖、名称依赖和控制依赖。1.数据依赖:1)指令i生成的结果可能会被指令j用到。2)指令j数据依赖于指令k,......
  • Leetcode刷题笔记——二分法
    二分法是搜索算法中极其典型的方法,其要求输入序列有序并可随机访问。算法思想为输入:有序数组nums,目的数值target要求输出:如果target存在在数组中,则输出其index,否则输出-1将原数组通过[left,right]两个索引划分范围,初值left=0,right=数组的最后一个元素当left<=right时mid......
  • 旧笔记本秒变web服务器---nat123 一款优秀的内网穿透服务器
    2014买的第一台笔记本,win7系统,加过内存,重装过多次系统但是无法运行win10,用来开发已经相当吃力,但运行还是比较流畅的,扔掉可惜,卖二手也卖不了多少,后来经过多次的思考与尝试,将厚重的光驱位扩展了500G硬盘,安装了winNAS,将其改装成了私有NAS网盘,但是客户端只有手机端app,对于我这做web开......
  • [学习笔记] 莫队
    一、普通莫队莫队是一种基于分块的算法,由莫队提出(orz)。莫队可以解决一类查询序列区间信息的题。可以使用该算法的前提是维护的信息必须可以在\(O(1)\)(如果用map/set可以带\(\log\),或者优化成\(O(1)\))内将\([l,r]\)的答案扩展到\([l-1,r],[l+1,r],[l,r-......
  • windows10,编译rust程序到so文件,供android调用,笔记
    1、用D:\myProgram\android_sdk\ndk\ndk-22.0.7026061\ndk-build.cmd编译,全路径,只写ndk-build,似乎不行2、在androidas里编译,提示soisnotaABI,其实是so放错地方了。应该放在src\main\jniLibs\arm64-v8a目录下(其他cpu类似),我就是缺少arm64-v8a目录,导致这个错误,新建arm64-v8......
  • 『学习笔记』狄利克雷生成函数
    定义一般地,对于一个函数\(f\),定义它的狄利克雷生成函数(简写为DGF)为:\[\tilde{F}(x)=\sum_{i\ge1}^\infty\dfrac{f_i}{i^x}.\]即:\[\tilde{F}(x)=f_1+\dfrac{f_2}{i^2}+\dfrac{f_3}{i^3}+\dfrac{f_4}{i^4}+\cdots.①\]性质若\(f\)是积性函数,则一定满足:......
  • 《C++并发编程实战》读书笔记(1):线程管控
    1、线程的基本管控包含头文件<thread>后,通过构建std::thread对象启动线程,任何可调用类型都适用于std::thread。voiddo_some_work();structBackgroundTask{voidoperator()()const;};//空的thread对象,不接管任何线程函数std::threadt1;//传入普通函数std::thr......
  • 前端学习笔记202308学习笔记第七十捌天-Map之8
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Map</title></hea......
  • 前端学习笔记202308学习笔记第七十捌天-Map之7
         ......