把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5068】[Ynoi2015] 我回来了(线段树)

点此看题面

  • 有一个集合,初始为空,要求支持两种操作:加入一个\([1,n]\)的数;对于所有\(i\in [L,R]\),求出最小的\(p\)满足集合中不存在\([i\times(p-1)+1,i\times p]\)中的数,输出这些\(k\)的和。
  • \(n\le10^5,q\le10^6\)

调和级数分析答案上界

众所周知,\(\sum_{i=1}^n\lfloor\frac ni\rfloor≈n\ln n\)

所以,答案总变化值实际上不会超过\(n\ln n\)

因此对于每一个数,我们可以暴力修改它的\(p\),只要用树状数组实现单点修改、区间求和即可。

线段树维护贡献区间

对于每一个\(i\),初始有一个答案区间\([1,i]\),方便起见我们把它表示到线段树上,将\(i\)扔入对应的\(vector\)里。

然后,每当插入一个数,就是把它到叶节点沿途所有节点中的\(vector\)清空,把\(vector\)中的元素的\(p\)加上\(1\)

注意,由于一个区间会被拆成多个,而我们并不容易从\(vector\)中删除一个数,因此\(vector\)中需要记录对应的是这个元素的哪个区间,并和该元素的当前区间比较过后再更新。

更新的时候,自然是暴力将\(p\)\(1\),但在扔到线段树上之前先询问一下这个区间内是否已存在数,如果存在可以继续将\(p\)\(1\),否则才扔到线段树上。

总体而言还是比较简单的,算是相对清新的\(Ynoi\)了。

代码:\(O(nlog^2n+qlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define pb push_back
using namespace std;
int n,p[N+5];
struct BIT {int a[N+5];I void U(RI x) {W(x<=n) ++a[x],x+=x&-x;}I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}}T;//树状数组
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
class SegmentTree
{
	private:
		#define PT CI l=1,CI r=n,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		int P[N<<2];vector<pair<int,int> > V[N<<2];vector<pair<int,int> >::iterator it;
	public:
		I void K(CI L,CI R,Con pair<int,int>& v,PT)//插入一个贡献区间[L,R]
		{
			if(L<=l&&r<=R) return (void)V[rt].pb(v);RI mid=l+r>>1;L<=mid&&(K(L,R,v,LT),0),R>mid&&(K(L,R,v,RT),0);//扔入vector
		}
		I void U(CI x,PT)//插入一个元素
		{
			for(it=V[rt].begin();it!=V[rt].end();++it) it->second==p[it->first]&&(F5(it->first),0);//遍历vector更新
			if(P[rt]=1,V[rt].clear(),l==r) return;RI mid=l+r>>1;x<=mid?U(x,LT):U(x,RT);
		}
		I int Q(CI L,CI R,PT)//询问区间中是否已有元素
		{
			if(!P[rt]||L<=l&&r<=R) return P[rt];RI mid=l+r>>1;return (L<=mid?Q(L,R,LT):0)||(R>mid?Q(L,R,RT):0);
		}
		I void F5(CI x)//更新
		{
			++p[x],T.U(x);W(x*(p[x]-1)+1<=n&&Q(x*(p[x]-1)+1,min(x*p[x],n))) ++p[x],T.U(x);//如果已有可以直接移动指针
			x*(p[x]-1)+1<=n&&(K(x*(p[x]-1)+1,min(x*p[x],n),make_pair(x,p[x])),0);//如果还没越界,扔到线段树上
		}
}S;
int main()
{
	RI Qt,i,j;for(read(n,Qt),i=1;i<=n;++i) p[i]=1,T.U(i),S.K(1,i,make_pair(i,1));//初始区间
	RI op,x,y;W(Qt--) read(op,x),op==1?S.U(x):(read(y),writeln(T.Q(y)-T.Q(x-1)));return clear(),0;
}
posted @ 2021-04-01 17:34  TheLostWeak  阅读(46)  评论(0编辑  收藏  举报