P3863 序列 题解

Luogu

Description.

维护序列,支持:

  1. 区间加
  2. 单点查询这个数历史版本有多少个 \(\le K\)

Solution.

只有第二个操作都需要一个树套树。
所以我们可以往 \(\sqrt\,\) 数据结构想。
但是这个分块方式是真的想不到。

考虑只有一个数的做法。
相当于要查询时间轴上有多少数 \(\le K\)
离线然后按照时间分块,查询直接二分就行了。
复杂度 \(O(Q\sqrt Q\log(\sqrt Q))=O(Q\sqrt Q\log Q)\)

然后考虑多个数,可以离线处理关于每个数的询问。
可以考虑差分然后前缀和,从左往右扫,在 \(l\)\(+v\),在 \(r+1\)\(-v\)
在时间上 \(+\) 操作可以转化为时间轴上的后缀加,分块是可以处理这件事的。
然后就做完了。

Coding.

点击查看代码
//是啊……你就是那只鬼了……所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=100005,B=316;int n,m,a[N];
struct qry{int p,t,w;}q[N];int qt;ll rs[N];
struct chg{int p,t;ll v;}c[N<<2];int ct;
namespace block
{
	int n,bl[N],L[N/B+5],R[N/B+5],ln[N/B+5],blt;
	ll vl[N/B+5][B+5],tg[N/B+5],a[N];
	inline void init()
	{
		n=::m;for(int i=0;i<=n;i++) bl[i]=i/B+1;
		for(int i=0;i<=n;i++) (L[bl[i]]?0:L[bl[i]]=i),R[bl[i]]=i;
		blt=bl[n];for(int i=1;i<=blt;i++) ln[i]=R[i]-L[i]+1;
	}
	inline int qrybl(int id,ll w) {return ln[id]-(lower_bound(vl[id]+1,vl[id]+ln[id]+1,w-tg[id])-vl[id])+1;}
	inline void pushdw(int id)
	{
		ll v;if(!tg[id]) return;else v=tg[id],tg[id]=0;
		for(int i=L[id];i<=R[id];i++) a[i]+=v;
		for(int i=1;i<=ln[id];i++) vl[id][i]+=v;
	}
	inline void rebuild(int id)
	{
		for(int i=L[id];i<=R[id];i++) vl[id][i-L[id]+1]=a[i];
		sort(vl[id]+1,vl[id]+ln[id]+1);
	}
	inline int query(int l,int r,ll k)
	{
		if(l>r) return 0;
		int le=bl[l],ri=bl[r],rs=0;pushdw(le);if(le^ri) pushdw(ri);
		if(le==ri) {for(int i=l;i<=r;i++) rs+=(a[i]>=k);return rs;}
		for(int i=l;i<=R[le];i++) rs+=(a[i]>=k);
		for(int i=L[ri];i<=r;i++) rs+=(a[i]>=k);
		for(int i=le+1;i<ri;i++) rs+=qrybl(i,k);
		return rs;
	}
	inline void modif(int l,int r,ll k)
	{
		int le=bl[l],ri=bl[r];pushdw(le);if(le^ri) pushdw(ri);
		if(le==ri) {for(int i=l;i<=r;i++) a[i]+=k;return rebuild(le);}
		for(int i=l;i<=R[le];i++) a[i]+=k;
		for(int i=L[ri];i<=r;i++) a[i]+=k;
		for(int i=le+1;i<ri;i++) tg[i]+=k;
		rebuild(le),rebuild(ri);
	}
}
int main()
{
	read(n,m),block::init();
	for(int i=1;i<=n;i++) read(a[i]),c[++ct]=(chg){i,0,a[i]-a[i-1]};
	for(int i=1,fg,l,r;i<=m;i++)
	{
		read(fg,l,r);ll w;if(!(fg&1)) q[++qt]=(qry){l,i,r};
		else read(w),c[++ct]=(chg){l,i,w},c[++ct]=(chg){r+1,i,-w};
	}memset(rs,-1,sizeof(rs));
	sort(q+1,q+qt+1,[&](qry a,qry b){return a.p<b.p;});
	sort(c+1,c+ct+1,[&](chg a,chg b){return a.p<b.p;});
	for(int i=1,cc=1,qq=1;i<=n;i++)
	{
		while(c[cc].p==i) block::modif(c[cc].t,m,c[cc].v),cc++;
		while(q[qq].p==i) rs[q[qq].t]=block::query(0,q[qq].t-1,q[qq].w),qq++;
	}
	for(int i=1;i<=m;i++) if(~rs[i]) printf("%lld\n",rs[i]);
	return 0;
}
posted @ 2021-11-10 18:43  Peal_Frog  阅读(41)  评论(0编辑  收藏  举报