[牛客周赛]未曾设想的道路
未曾设想的道路
题解
线段树板子题
我们考虑建一棵线段树,每个点维护对应区间曾经出现过的历史前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大数。
 我们查询的时候就找到这个区间的前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大值,输出第 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k个。
 显然,对于线段树上的每个点,我们需要一个维护前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大的序列,且维护的序列得是有序的。
 很明显,在维护序列有序的前提下加入一个数是 
     
      
       
       
         O 
        
        
        
          ( 
         
        
          k 
         
        
          ) 
         
        
       
      
        O\left(k\right) 
       
      
    O(k)的,合并两个数列也是 
     
      
       
       
         O 
        
        
        
          ( 
         
        
          k 
         
        
          ) 
         
        
       
      
        O\left(k\right) 
       
      
    O(k)的。
 询问就将所有的询问区间的前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大值的序列合并起来找第 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大即可。
但我们加上修改的操作该怎么办呢?
 单纯的修改区间很简单,我们可以通过对修改的区间加上懒标记来进行维护。
 但很明显,由于要维护下传的区间的历史的前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大,我们不能直接加懒标记,因为 
     
      
       
       
         x 
        
       
      
        x 
       
      
    x有正有负,但我们可以考虑记录下历史前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大的懒标记值。
 同时,我们显然也需要知道当前区间中下传懒标记的前 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大数。
 如果直接把这 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k个一一配对时间复杂度会达到 
     
      
       
       
         O 
        
        
        
          ( 
         
         
         
           k 
          
         
           2 
          
         
        
          ) 
         
        
       
      
        O\left(k^2\right) 
       
      
    O(k2),也是不可取的。
 我们可以先加两者第一大的两个数加进去,明显,接下来的可能最大值就是懒标记最大加区间第二大或懒标记第二大加区间最大,我们可以通过优先队列对其进行维护。
 这样,下传懒标记的过程时间复杂度就是 
     
      
       
       
         O 
        
        
        
          ( 
         
        
          k 
         
        
          l 
         
        
          o 
         
        
          g 
          
        
          k 
         
        
          ) 
         
        
       
      
        O\left(klog\,k\right) 
       
      
    O(klogk)了。
而懒标记下传对下面序列懒标记的影响,我们只需要将下传的历史 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大懒标记序列加上下面一个点懒标记的值,再与其的历史 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k大懒标记序列合并即可。
 区间最大值直接加上懒标记就行了。
我们对应的询问与修改操作就这样解决了。
 由于线段树上修改与查询操作会对应的区间有 
     
      
       
       
         l 
        
       
         o 
        
       
         g 
         
       
         n 
        
       
      
        log\,n 
       
      
    logn个,所以我们单次询问与修改操作的时间复杂度是 
     
      
       
       
         O 
        
        
        
          ( 
         
        
          k 
         
        
          l 
         
        
          o 
         
        
          g 
          
        
          k 
         
        
          l 
         
        
          o 
         
        
          g 
          
        
          n 
         
        
          ) 
         
        
       
      
        O\left(klog\,klog\,n\right) 
       
      
    O(klogklogn)。
 总时间复杂度 
     
      
       
       
         O 
        
        
        
          ( 
         
        
          m 
         
        
          k 
         
        
          l 
         
        
          o 
         
        
          g 
          
        
          n 
          
        
          l 
         
        
          o 
         
        
          g 
          
        
          k 
         
        
          + 
         
        
          n 
         
        
          k 
         
        
          l 
         
        
          o 
         
        
          g 
          
        
          n 
         
        
          ) 
         
        
       
      
        O\left(mklog\,n\,log\,k+nklog\,n\right) 
       
      
    O(mklognlogk+nklogn)。
 
      
       
        
        
          D 
         
        
          e 
         
        
          v 
         
        
          i 
         
        
          l 
         
        
       
         Devil 
        
       
     Devil魔鬼大佬有一种 
      
       
        
        
          O 
         
         
         
           ( 
          
         
           ( 
          
         
           m 
          
         
           + 
          
         
           n 
          
         
           ) 
          
         
           k 
          
          
          
            n 
           
          
         
           ) 
          
         
        
       
         O\left((m+n)k\sqrt{n}\right) 
        
       
     O((m+n)kn)的分块做法,虽然看起来会 
      
       
        
        
          T 
         
        
       
         T 
        
       
     T但居然过了,而且比线段树跑得还快,是官方数据太水了吧。
源码
看起来思路很简单,但真的很难调。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int lim=10000000;
const int jzm=1e6+7;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<LL,LL> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
struct Array{
	int sta[105],stak;Array(){stak=0;}void clear(){stak=0;}
	void insert(int x){
		stak++;
		for(int i=1;i<=stak;i++)if(sta[i]<x||i==stak)
			{for(int j=stak-1;j>=i;j--)swap(sta[j],sta[j+1]);sta[i]=x;break;}
		if(stak>100)sta[stak--]=0;
	}
	void update(int aw){for(int i=1;i<=stak;i++)sta[i]+=aw;}
};
Array merge(Array x,Array y){
	Array res;int i=1,j=1;
	while(i<=x.stak&&j<=y.stak&&res.stak<100)
		if(x.sta[i]>y.sta[j])res.sta[++res.stak]=x.sta[i++];
		else res.sta[++res.stak]=y.sta[j++];
	while(i<=x.stak&&res.stak<100)res.sta[++res.stak]=x.sta[i++];
	while(j<=y.stak&&res.stak<100)res.sta[++res.stak]=y.sta[j++];
	return res;
}
int n,m,a[MAXN];
struct node{
	int x,y,val;node(){x=y=val=0;}node(int X,int Y,int V){x=X;y=Y;val=V;}
	bool friend operator < (const node &x,const node &y){return x.val<y.val;}
};
priority_queue<node>q;
struct ming{Array hislzy,hismx,mx;int lzy;};
class SegmenTree{
	private:
		ming tr[MAXN<<2];
		void pushup(int rt){
			tr[rt].hismx=merge(tr[rt<<1].hismx,tr[rt<<1|1].hismx);
			tr[rt].mx=merge(tr[rt<<1].mx,tr[rt<<1|1].mx);
		}
		void pushdown(int rt){
			if(tr[rt].hislzy.stak){
				while(!q.empty())q.pop();Array tmp;tmp.clear();
				q.push((node){1,1,tr[rt<<1].mx.sta[1]+tr[rt].hislzy.sta[1]});
				while(!q.empty()&&tmp.stak<100){
					node t=q.top();q.pop();tmp.sta[++tmp.stak]=t.val;
					if(t.x<tr[rt<<1].mx.stak&&t.y==1)q.push(node(t.x+1,1,tr[rt<<1].mx.sta[t.x+1]+tr[rt].hislzy.sta[1]));
					if(t.y<tr[rt].hislzy.stak)q.push(node(t.x,t.y+1,tr[rt<<1].mx.sta[t.x]+tr[rt].hislzy.sta[t.y+1]));
				}
				tr[rt<<1].hismx=merge(tmp,tr[rt<<1].hismx);tmp.clear();while(!q.empty())q.pop();
				q.push((node){1,1,tr[rt<<1|1].mx.sta[1]+tr[rt].hislzy.sta[1]});
				while(!q.empty()&&tmp.stak<100){
					node t=q.top();q.pop();tmp.sta[++tmp.stak]=t.val;
					if(t.x<tr[rt<<1|1].mx.stak&&t.y==1)q.push(node(t.x+1,1,tr[rt<<1|1].mx.sta[t.x+1]+tr[rt].hislzy.sta[1]));
					if(t.y<tr[rt].hislzy.stak)q.push(node(t.x,t.y+1,tr[rt<<1|1].mx.sta[t.x]+tr[rt].hislzy.sta[t.y+1]));
				}
				tr[rt<<1|1].hismx=merge(tmp,tr[rt<<1|1].hismx);tmp.clear();
				Array t=tr[rt].hislzy;t.update(tr[rt<<1].lzy);tr[rt<<1].hislzy=merge(tr[rt<<1].hislzy,t);
				t=tr[rt].hislzy;t.update(tr[rt<<1|1].lzy);tr[rt<<1|1].hislzy=merge(tr[rt<<1|1].hislzy,t);
				tr[rt<<1].mx.update(tr[rt].lzy);tr[rt<<1|1].mx.update(tr[rt].lzy);
				tr[rt<<1].lzy+=tr[rt].lzy;tr[rt<<1|1].lzy+=tr[rt].lzy;tr[rt].hislzy.clear();tr[rt].lzy=0;
			}
		}
	public:
		void build(int rt,int l,int r){
			if(l==r){tr[rt].hismx.insert(a[l]);tr[rt].mx.insert(a[l]);return ;}
			int mid=(l+r)>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);pushup(rt);
		}
		void modify(int rt,int l,int r,int al,int ar,int aw){
			if(l>r||l>ar||r<al||al>ar)return ;
			if(al<=l&&r<=ar){
				tr[rt].mx.update(aw);tr[rt].hismx=merge(tr[rt].hismx,tr[rt].mx);
				tr[rt].lzy+=aw;tr[rt].hislzy.insert(tr[rt].lzy);return ;
			}
			int mid=(l+r)>>1;pushdown(rt);
			if(al<=mid)modify(rt<<1,l,mid,al,ar,aw);
			if(ar>mid)modify(rt<<1|1,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		Array query(int rt,int l,int r,int al,int ar){
			Array res;res.clear();if(l>r||al>r||ar<l||al>ar)return res;
			if(al<=l&&r<=ar)return tr[rt].hismx;pushdown(rt);int mid=(l+r)>>1;
			if(ar<=mid)return query(rt<<1,l,mid,al,ar);
			if(al>mid)return query(rt<<1|1,mid+1,r,al,ar);
			return merge(query(rt<<1,l,mid,al,ar),query(rt<<1|1,mid+1,r,al,ar));
		}
}T;
signed main(){
	read(n);read(m);
	for(int i=1;i<=n;i++)read(a[i]);T.build(1,1,n);
	for(int i=1;i<=m;i++){
		int typ,l,r,k;read(typ);read(l);read(r);read(k);
		if(!typ)T.modify(1,1,n,l,r,k);
		else{
			Array res=T.query(1,1,n,l,r);
			k=min(res.stak,k);printf("%d\n",res.sta[k]);
		}
	}
	return 0;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号