1. 程式人生 > 其它 >P3863 序列 題解

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;
}