zoukankan      html  css  js  c++  java
  • 题解 Luogu P3863 序列

    题意

    给定一个长度为 \(n\) 的序列,给出 \(q\) 个操作,形如:

    • \(1~l~r~x\) 表示将序列下标介于 \([l,r]\) 的元素加上 \(x\) (请注意,\(x\) 可能为负)

    • \(2~p~y\) 表示查询 \(a_p\) 在过去的多少秒时间内不小于 \(y\)

    开始时为第 \(0\) 秒,第 \(i\) 个操作发生在第 \(i\) 秒。

    \(2≤n,q≤100000, 1 \leq l \leq r \leq n,1 \leq p \leq n,-10^9 \leq x,y,a_i \leq 10^9\)

    题解

    如果只有一个数,似乎就很好维护了。

    维护每一个时刻这个数加上的值。查询某一段时间里大于某个值的时间数量。将时间分块就可以做了。

    但如果是 \(n\) 个数呢?如果在线搞的话,似乎并不能很好的维护。那么离线下来,给询问排序,依次处理就好了。

    那怎么处理区间修改操作呢?观察到如果在 \(t\) 时刻给 \([l,r]\) 加上 \(v\),会对处理 \([l,r]\) 中每一个数时都造成同样的影响。所以将每一个修改操作分成两部分:

    • 第一部分,在处理第 \(l\) 个数的时候将时刻 \([t,m]\) 都加上 \(v\)
    • 第二部分,在处理第 \(r+1\) 个数的时候将时刻 \([t,m]\) 都减去 \(v\),抵消影响(因为这个操作不会对 \(r+1\) 及其之后的数造成影响,故减去)。

    (是不是感觉有点像扫描线呢)

    这样我们就可以得到每一个数每个时刻被加上的值。

    处理第 \(i\) 个数第 \(t\) 秒的询问时,分块查询 \([0,t-1]\) 有多少个时刻大于等于 \(y - a_i\) 即可。

    注意到最后输出结果时是按照输入顺序输出,所以还要处理一下询问的顺序。

    # include <bits/stdc++.h>
    # define rr register
    # define int long long
    const int N=100010;
    struct Line{//修改
    	int x;
    	int Time;
    	int v;
    }a[N<<1];
    struct Asker{//查询
    	int x,v; 
    	int Time;
    	int Index;// 记录是第几次询问
    }ask[N];
    int cnta,cntb;//修改数量 & 查询数量
    int ans[N]; // 存储每一次询问的答案
    int val[N];
    int n,m;
    /* 分块部分 */
    int tseque[N];
    int fseque[N];
    int add[N];
    int Kuai[N];
    int KL[N],KR[N];
    /* 分块部分 */
    int siz;// 要分的块大小
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')	
    		res=res*10+c-48;
    	return res*f;		
    }
    inline bool cmp_Line(Line X,Line Y){//给修改排序
    	return X.x!=Y.x?X.x<Y.x:X.Time<Y.Time;
    }
    inline bool cmp_Ask(Asker X,Asker Y){//给询问排序
    	return X.x!=Y.x?X.x<Y.x:X.Time<Y.Time;
    }
    inline bool cmp_Integer(int X,int Y){//为了给块内元素从大到小排序用的
    	return X>Y;
    }
    inline void resort(int x){//每次修改之后,块内元素需要重新排序
    	for(rr int i=KL[x];i<=KR[x];++i)
    		fseque[i]=tseque[i];
    	std::sort(fseque+KL[x],fseque+KR[x]+1,cmp_Integer);
    	return;	
    }
    inline void change(int l,int r,int v){// 分块修改操作
    	l=std::max(l,0ll);
    	r=std::min(r,m);
    	if(Kuai[l]==Kuai[r]){
    		for(rr int i=l;i<=r;++i){
    			tseque[i]+=v;
    		}
    		resort(Kuai[l]);
    		return;
    	}
    	for(rr int i=l;i<=KR[Kuai[l]];++i)
    		tseque[i]+=v;
    	resort(Kuai[l]);
    	for(rr int i=r;i>=KL[Kuai[r]];--i)
    		tseque[i]+=v;
    	resort(Kuai[r]);
    	for(rr int i=Kuai[l]+1;i<=Kuai[r]-1;++i){
    		add[i]+=v;
    	}
    	return;
    }
    inline int query(int l,int r,int v){// 分块查询操作
    	int cnt=0;
    	if(Kuai[l]==Kuai[r]){
    		for(rr int i=l;i<=r;++i)
    			if(tseque[i]+add[Kuai[i]]>=v)
    				++cnt;
    		return cnt;		
    	}
    	for(rr int i=l;i<=KR[Kuai[l]];++i)
    		if(tseque[i]+add[Kuai[i]]>=v)
    			++cnt;
    	for(rr int i=r;i>=KL[Kuai[r]];--i)
    		if(tseque[i]+add[Kuai[i]]>=v)
    			++cnt;
    	for(rr int i=Kuai[l]+1;i<=Kuai[r]-1;++i){
    		int L=KL[i],R=KR[i],ans=KL[i]-1;
    		while(L<=R){
    			int mid=(L+R)>>1;
    			if(fseque[mid]+add[Kuai[mid]]>=v){
    				ans=mid;
    				L=mid+1;
    			}else{
    				R=mid-1;
    			}
    		}
    		cnt+=(ans-KL[i])+1;
    	}
    	return cnt;
    }
    # undef int
    int main(void){
    # define int long long
    	n=read(),m=read();
    	for(rr int i=1;i<=n;++i){
    		val[i]=read();
    	}
    	for(rr int i=1,opt;i<=m;++i){
    		opt=read();
    		if(opt==1){
    			int l=read(),r=read(),v=read();
    			a[++cnta].x=l;
    			a[cnta].Time=i;
    			a[cnta].v=v;
    			a[++cnta].x=r+1;
    			a[cnta].Time=i;
    			a[cnta].v=-v;
    		}else{
    			int p=read(),y=read();
    			ask[++cntb].x=p;
    			ask[cntb].Index=cntb;
    			ask[cntb].v=y;
    			ask[cntb].Time=i;
    		}
    	}
    	std::sort(a+1,a+1+cnta,cmp_Line);
    	std::sort(ask+1,ask+1+cntb,cmp_Ask);// 读入、存储并排序每一个操作
    	siz=sqrt(m);
    	for(rr int i=0;i<=m;++i){
    		Kuai[i]=i/siz+1;
    	}
    	for(rr int i=1;(i-1)*siz<=m;++i){
    		KL[i]=(i-1)*siz;
    		KR[i]=std::min(i*siz-1,m);
    	}
    	int now=1;
    	for(rr int i=1;i<=cntb;++i){
    		while((a[now].x<ask[i].x||(a[now].x==ask[i].x&&a[now].Time<ask[i].Time))&&now<=cnta){
    			change(a[now].Time,m,a[now].v);
    			++now;
    		}
    		ans[ask[i].Index]=query(0,ask[i].Time-1,ask[i].v-val[ask[i].x]);
    	}
    	for(rr int i=1;i<=cntb;++i)
    		printf("%lld\n",ans[i]);
    	return 0;
    } 
    
  • 相关阅读:
    玩转JavaScript module pattern精髓
    玩转Javascript 给JS写测试
    Feature Toggle JUnit
    状态机模式实战
    Java静态类
    Guava增强for循环
    Spring Security使用心得
    听个响
    Geoserver2.16.2初步使用
    GeoWebCache1.10.5发布arcgis瓦片服务
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/11949273.html
Copyright © 2011-2022 走看看