首页 > 其他分享 >多阶前缀和学习笔记

多阶前缀和学习笔记

时间:2023-08-26 16:23:20浏览次数:29  
标签:ri le 前缀 limits ll 笔记 wz 多阶 sum

例题传送门:P4062 [Code+#1] Yazid 的新生舞会

简要题意:给定一串序列\(A_1,A_2,...,A_n\),求有多少个子区间\([l,r]\)满足子区间内众数的个数大于\(\frac{r-l+1}{2}\)

思考若数\(w\)是众数的方案数:新建一个序列\(B\),令\(B_i=[A_i=w]\),考虑前缀和\(S_i=\sum\limits_{k=1}^iB_i=S_{i-1}+B_i\),则对于子区间\([l+1,r]\),若\(S_r-S_l>\frac{r-l}{2}\),则数\(w\)是子区间\([l+1,r]\)的众数,转换一下该不等式为:\(2S_r-r>2S_l-l\),则可以线性求\(2S_i-i\),可以用树状数组寻找小于\(2S_r-r\)的数,则求数\(w\)是众数的方案数时间复杂度为\(O(n \log n)\)。

因为\(0 \le A_i \le n-1\),所以用该方法时间复杂度为\(O(n^2 \log n)\)

这样还不够,还要优化。

如上图,我们发现:\(2S_i-i\)是由一个个等差数列组成的,而等差数列的个数为原序列中\(1\)的个数\(+1\),也就是\(A_i=w\)的个数\(+1\),所以一共有的等差数列的个数不超过\(2n\)个,我们可以考虑从这里入手优化:只要我们快速求出一个等差数列对答案的贡献就好了

还有一个可以思考的点:\(P_i\in [-n,n]\)

因为负数不太好计算,所以我们令所有\(P_i\)加上\(n+1\),即\(P_i\in [1,2n+1]\)

考虑差分计数(即\(p_1+p_2+...p_i\)表示\(P_i\)的出现次数,下同),记\(\sum\limits_{k=1}^ip_k\)为\(P_i\)的出现次数,对于等差数列\(le,le+1,...,ri-1,ri\),令\(p_{le}++,p_{ri+1}--\)

考虑差分计数,记\(\sum\limits_{k=1}^itr_k\)为\(P_1\sim P_i\)出现次数和,则可以推出\(tr_i=\sum\limits_{j=1}^ip_j\)

考虑差分计数,记\(\sum\limits_{k=1}^ia_k\)为\(P_1,P_1\sim P_2,P_1\sim P_3...P_1\sim P_i\)的出现次数的和,则可以推出\(a_i=\sum\limits_{j=1}^itr_j\)

考虑求\(P_1\sim P_{le},P_1\sim P_{le+1}...P_1\sim P_{ri-1},P_1\sim P_{ri}\)出现次数的和,即求\(\sum\limits_{i=1}^{ri}a_i-\sum\limits_{i=1}^{le-1}a_i\)

理论上说只要我们会求快速求\(\sum\limits_{i=1}^xa_i\)就可以了

\[\sum\limits_{i=1}^xa_i=\sum\limits_{i=1}^x\sum\limits_{j=1}^itr_j=\sum\limits_{i=1}^x\sum\limits_{j=1}^i\sum\limits_{k=1}^jp_k \]

转换一下原式,把\(p_k\)提到前面来:

\[\sum\limits_{k=1}^xp_k\sum\limits_{j=k}^x\sum\limits_{i=j}^x1=\sum\limits_{k=1}^xp_k\sum\limits_{j=k}^x(x-j+1)=\sum\limits_{k=1}^xp_k\frac{(x-k+1)(x-k+2)}{2} \]

把多项式按照拆开来,整理一下为:

\[\frac{1}{2}\sum\limits_{k=1}^x[(x^2+3x+2)\times p_k-(3+2x)\times p_k\times k+p_k\times k^2] \]

只要我们用树状数组记录\(p_k,p_k\times k,p_k\times k^2\),就可以在\(O(\log n)\)时间复杂度内求出\(\sum\limits_{i=1}^xa_i\)

插入时也是\(O(\log n)\)的时间复杂度(树状数组单点修改\(p_{le},p_{ri+1}\))

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e5+50;
ll n,type,a,le,ri,last,ans,en;
vector<ll> e[N];
ll tr[N*2],tr1[N*2],tr2[N*2];
ll lb(ll x)
{
	return x & -x;
}
void add(ll wz,ll x)
{
	ll now=wz;
	while(wz<=en)
	{
		tr[wz]=tr[wz]+x;
		tr1[wz]=tr1[wz]+x*now;
		tr2[wz]=tr2[wz]+x*now*now;
		wz+=lb(wz);
	}
}
ll query(ll wz)
{
	ll re=0,now=wz;
	while(wz>0)
	{
		re=re+now*now*tr[wz]+3*now*tr[wz]+2*tr[wz];
		re=re+tr2[wz];
		re=re-3*tr1[wz]-2*now*tr1[wz];
		wz-=lb(wz);
	}
	return re/2;
}
int main()
{
	scanf("%lld %lld",&n,&type);
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&a);
		e[a].push_back(i);
	}
	en=2*n+1;
	for(ll i=0;i<n;i++)
	{
		e[i].push_back(n+1);
		ri=n+1;//[-n,n]->[1,2n+1]
		last=1;
		for(ll j=0;j<e[i].size();j++)
		{
			le=ri-(e[i][j]-last);
			ans=ans+query(ri-1)-query(le-2);
			add(le,1);
			add(ri+1,-1);
			ri=le+1;
			last=e[i][j]+1;
		}
		ri=n+1;
		last=1;
		for(ll j=0;j<e[i].size();j++)
		{
			le=ri-(e[i][j]-last);
			add(le,-1);
			add(ri+1,1);
			ri=le+1;
			last=e[i][j]+1;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

总结一下:

1,只需要\(n\)个树状数组就可以求出\(n\)阶前缀和

2,遇到这种很有规律的计数题,不一定要直接计数,可以考虑差分计数,最后再用\(n\)阶前缀和快速求出即可

标签:ri,le,前缀,limits,ll,笔记,wz,多阶,sum
From: https://www.cnblogs.com/pengchujie/p/17658962.html

相关文章

  • 失配树学习笔记
    传送门考虑把原字符串先\(kmp\)一遍,求出以\(i\)结尾的前缀的最长\(border\),根据\(border\)的\(border\)还是\(border\)这个定理,我们在寻找前缀\(p\)和前缀\(q\)的最长公共\(border\)时,我们可以考虑运用这个定理,一直往前跳,找到最先一样的\(border\),这就是最长公共\(bordedr\),至于......
  • Dirichlet 前缀和学习笔记
    传送门求\(b_k=\sum\limits_{i|k}{a_i}\)考虑\(i=p_1^k,j=p_1^{k+1}\),若我们已经求出了\(b_i\),则易知\(b_j=b_i+a_j\)然后根据上面的方法,考虑对于所有的\(k=p_1^{k1}p_2^{k2}...p_l^{kl}\),若\(j=p_2^{k2}...p_l^{kl}\),我们已经求出所有的\(c_k=a_j+a_{p1\timesj}+a_{p1^2\time......
  • 回文自动机(PAM)学习笔记
    传送门我认为理解回文自动机需要图,以\(abbaabba\)为例,它的回文树是这样的:令树上的每一个点为一个回文串,其中,\(1\)为根的树中的点回文串长度为奇数,且最中间的那个字母就是\(1\)连向其他点的的边的字母,而\(0\)为根的树中的点回文串长度为偶数。举点例子吧:点\(2\)的回文串为\(a\)......
  • c语言笔记6
    c语言笔记6(结构体,共用体,枚举,文件操作,makefile)1.结构体1.1结构体的概念结构体也是构造类型之一,由至少一个基本数据类型或构造类型组成的一种数据结构(集合),这种数据结构称之为结构体1.2结构体的定义使用结构体之前,先定义结构体,然后使用这个结构体时作为一种数据类型(......
  • 欧拉定理学习笔记
    欧拉定理:若\(gcd(a,m)=1\),则\(a^{\varphi(m)}\equiv1\pmod{m}\)证明:令\(r_1,r_2,···,r_{\varphi(m)}\)为模m下的一个简化剩余系,则\(ar_1,ar_2,···,ar_{\varphi(m)}\)也为模m下的一个简化剩余系,令\(f=r_1*r_2*···*r_{\varphi(m)}\),则有:\(f\equivar_1*ar_2*···*ar_{......
  • 杜教筛学习笔记
    杜教筛学习笔记闲话感觉以前根本没学懂杜教筛,于是重学了一遍,写个笔记记录一下。前置知识依赖于迪利克雷卷积、莫比乌斯反演、整除分块相关知识。记号约定及基本性质约定:\(f*g\)表示\(f\)与\(g\)的迪利克雷卷积,即\((f*g)(n)=\sum\limits_{ij=n}f(i)g(j)\)。\(f\cdot......
  • Linux设备驱动开发详解——学习笔记
    Linux设备驱动概述计算机系统的运转需要软件和硬件共同参与,硬件是底层基础,软件则实现了具体的应用。硬件和软件之间则通过设备驱动来联系。在没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口。而在有操作系统的情况下,驱动的架构则由相应的操作系统来定义。驱动存......
  • Mybatis学习笔记
    一、Mybatis简介二、下载与实现1)下载 官网下载2)实现①创建项目工程,并加入依赖②创建核心configuration.xml配置文件,配置JDBC的连接信息③创建POJO对象④创建POJO对应的mapper映射文件⑤在configuration.xml文件中加载mapper文件⑥测试三、接口实现方式(项目中常用)①创建一个接口②......
  • 运筹学习笔记之列生成
    列生成算法介绍1.什么是列生成列生成算法是一种用于解决大规模线性规划问题的高效算法,它基于单纯形法的思想,通过求解子问题来找到可以进基的非基变量。在列生成算法中,每个变量都代表一列,因此称为列生成算法。该算法的优点在于其高效的计算性能和较好的收敛性,适用于处理大规模、......
  • openGauss学习笔记-51 openGauss 高级特性-列存储
    openGauss学习笔记-51openGauss高级特性-列存储openGauss支持行列混合存储。行存储是指将表按行存储到硬盘分区上,列存储是指将表按列存储到硬盘分区上。行、列存储模型各有优劣,建议根据实际情况选择。通常openGauss用于OLTP(联机事务处理)场景的数据库,默认使用行存储,仅对执行复杂......