Loading

P5356 [Ynoi2017]由乃打扑克

P5356 [Ynoi2017]由乃打扑克

CSP前写Ynoi是不是找死啊

不过这题比较简单,两天加起来一个小时就调完了。

首先先把序列分块了,设块长为 \(S\)

区间加直接打标记。

至于第二个操作,能想到的是先二分答案,然后判断有几个数比这个小。

那么我们需要在块内二分,所以要把每个块排序。这样在区间加的时候要重构角块。

两个二分套起来是 \(O(\dfrac{n}{S}\log|V|\log n)\)

考虑查询时如何处理角块:在排好序的数组里记录这个数在原数组的下标,每次扫一遍整个块排好序的数组,如果下标在 \([l,r]\) 内那么直接按顺序推进一个数组,这样二分的时候不用排序角块。这样复杂度是 \(O(S)\) 的,否则是 \(O(S\log S)\) 的,lxl卡掉了。

修改重构角块时注意,可以把修改的那一段和原来排好序的其余部分归并,不用排序。那么这里是复杂度是 \(O(S)\) ,否则这里是 \(O(S\log S)\) 的,lxl卡掉了。

那么总复杂度就是 \(O(S+\dfrac{n}{S}\log^2)\)\(S=n\log\) 最优

我发现我是次短解,2.5k一道Ynoi,还进了最优解第一页。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define mkp(x,y) make_pair(x,y)
#define fi first
#define se second
#define pb(x) push_back(x)
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?x:-x;
}
#define S 800
#define N 100005
int n,m,a[N],up,down;
typedef pair<int,int> pii;
pii b[N],t1[S],t2[S];
int num,L[N/5],R[N/S+5],bel[N];
int cnt1,cnt2,tag[N/S+5];
void init(){
	num=(n-1)/S+1;
	for(int i=1;i<=num;++i)L[i]=R[i-1]+1,R[i]=i*S;
	R[num]=n;
	for(int i=1;i<=num;++i){
		for(int j=L[i];j<=R[i];++j)
			bel[j]=i,b[j].fi=a[j],b[j].se=j;
		sort(b+L[i],b+R[i]+1);
	}
}
void rebuild(int id,int l,int r,int delta){
	cnt1=0,cnt2=0;
	for(int i=L[id];i<=R[id];++i)
		if(b[i].se>=l&&b[i].se<=r)t1[++cnt1]=mkp(b[i].fi+delta+tag[id],b[i].se);
		else t2[++cnt2]=mkp(b[i].fi+tag[id],b[i].se);
	tag[id]=0;
	int i=L[id],j=1,k=1;
	while(j<=cnt1&&k<=cnt2)b[i++]=t1[j]<t2[k]?t1[j++]:t2[k++];
	while(j<=cnt1)b[i++]=t1[j++];
	while(k<=cnt2)b[i++]=t2[k++];
}
void update(int l,int r,int k){
	k>0?up+=k:down+=k;
	int bl=bel[l],br=bel[r];
	if(bl==br){
		rebuild(bl,l,r,k);
		return;
	}
	for(int i=bl+1;i<br;++i)tag[i]+=k;
	rebuild(bl,l,R[bl],k),rebuild(br,L[br],r,k);
}
int calc(int l,int r,int k,pii*b){
	int ll=l,rr=r,ret=0;
	while(ll<=rr){
		int mid=(ll+rr)>>1;
		if(b[mid].fi<=k)ret=mid-l+1,ll=mid+1;
		else rr=mid-1;
	}
	return ret;
}
int ask(int l,int r,int k){
	int res=0;
	int bl=bel[l],br=bel[r];
	for(int i=bl+1;i<br;++i)res+=calc(L[i],R[i],k-tag[i],b);
	res+=calc(1,cnt1,k-tag[bl],t1);
	res+=calc(1,cnt2,k-tag[br],t2);
	return res;
}
int query(int l,int r,int k){
	if(k>r-l+1)return -1;
	int bl=bel[l],br=bel[r];
	if(bl==br){
		cnt1=0;
		for(int i=L[bl];i<=R[br];++i)if(b[i].se>=l&&b[i].se<=r)t1[++cnt1]=b[i];
		return t1[k].fi+tag[bl];
	}
	cnt1=0,cnt2=0;
	for(int i=L[bl];i<=R[bl];++i)if(b[i].se>=l&&b[i].se<=r)t1[++cnt1]=b[i];
	for(int i=L[br];i<=R[br];++i)if(b[i].se>=l&&b[i].se<=r)t2[++cnt2]=b[i];
	int ll=down,rr=up,res;
	while(ll<=rr){
		int mid=(ll+rr)>>1,t=ask(l,r,mid);
		if(t>=k)res=mid,rr=mid-1;
		else ll=mid+1;
	}
	return res;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)a[i]=read(),up=max(up,a[i]),down=min(down,a[i]);
	init();
	while(m--){
		int opt=read(),l=read(),r=read(),k=read();
		if(opt==1)printf("%d\n",query(l,r,k));
		else update(l,r,k);
	}
}
posted @ 2020-11-06 08:57  zzctommy  阅读(260)  评论(0编辑  收藏  举报