zoukankan      html  css  js  c++  java
  • 【洛谷5068】[Ynoi2015] 我回来了(线段树)

    点此看题面

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

    调和级数分析答案上界

    众所周知,(sum_{i=1}^nlfloorfrac ni floor≈nln n)

    所以,答案总变化值实际上不会超过(nln 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('
    ');}
    }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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    SqlDataAdapter 类
    新博开
    gcc编译C++程序

    抖动
    css3 导航效果
    javascript判断IE浏览器的版本
    CSS样式表继承详解
    css选择器
    鼠标样式
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5068.html
Copyright © 2011-2022 走看看