P5356 [Ynoi2017] 由乃打扑克

纪念一下人生第一道 Ynoi
题目链接

题意

是个人都看得懂吧。。。。。。

solution

看到 Ynoi,想到什么?

  • 分块
  • 卡常

你猜对了,这题又是分块。

数列分块入门 2中我们已经学会了询问区间内小于某个值 \(x\) 的元素个数。

很容易想到二分元素的值域,如果小于等于 \(x\) 的小于 \(k\),那么就往大枚举,否则往下枚举,更新答案。

code:

while(l <= r){
	int mid = (l+r)>>1;
	int check = query(dl, dr, mid);
	if(check < k)	l = mid+1;
	else r = mid-1, ans = mid;
}

看着非常地正确。但时间复杂度是 \(O(m \sqrt{n} \log{n} \log{v})\)

很遗憾,被我卡了 -lxl

优化

但是作为最简单的Ynoi,它是可以被以上暴力c过去的。

加上一下优化就可以过了:

  • 二分边界取区间最大和最小值
  • 查询时,如果当前区间的最大值小于 \(k\) 那么就可以直接加上区间大小,如果当前区间最小值大于 \(k\) 那么就不用再去计算它了。
  • 玄学的块长

代码

#include<cmath>
#include<cstdio>
#include<algorithm>
const int MAX = 100005;
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define rg register

static char buf[1000000],*p1=buf,*p2=buf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline int read(){int x=0,f=1;char c=getchar();while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+c-48;c=getchar();}return x*f;}
inline void write(int x){static char buf[20];static int len=-1;if(x<0)putchar('-'),x=-x;do buf[++len]=x%10,x/=10;while(x);while(len>=0)putchar(buf[len--]+48);}

int a[MAX];
int b[MAX];
int id[MAX]; 
int pls[1005];

int L[1005], R[1005];

inline void add(int l, int r, int c){
	int p = id[l], q = id[r];
	if(p == q){
		for(rg int i = l; i <= r; i++)	b[i] += c;
		for(rg int i = L[p]; i <= R[p]; i++){
			a[i] = b[i];
		}
		std::sort(a+L[p], a+R[p]+1);
	}else{
		for(rg int i = l; i <= R[p]; ++i)	b[i] += c;
		for(rg int i = L[p]; i <= R[p]; ++i)	a[i] = b[i];
		std::sort(a+L[p], a+R[p]+1);
		for(rg int i = p+1; i <= q-1; ++i)	pls[i] += c;
		for(rg int i = r; i >= L[q]; --i)	b[i] += c;
		for(rg int i = L[q]; i <= R[q]; ++i)	a[i] = b[i];
		std::sort(a+L[q], a+R[q]+1);
	}
}

inline int query(int l, int r, int c){
	int p = id[l], q = id[r];
	int ans = 0;
	if(p == q){
		if(a[R[p]] + pls[p] <= c)	ans += r-l+1;
		else if(a[L[p]] + pls[p] <= c)	for(int i = l; i <= r; ++i)	ans+=b[i] + pls[p] <= c;
	}else{
		if(a[R[p]] + pls[p] <= c)	ans += R[p]-l+1;
		else if(a[L[p]] + pls[p] <= c)  for(int i = l; i <= R[p]; i++)	ans+=b[i] + pls[p] <= c;
		int u,v,mid;
		for(rg int i = p+1; i <= q-1; ++i){
			if(a[L[i]]+pls[i]>c)continue;
			if(a[R[i]]+pls[i]<=c){
				ans+=R[i]-L[i]+1;
				continue;
			}
			u=L[i],v=R[i],mid;
			while(u<v){
				int mid=u+((v-u)>>1)+1;
				if(a[mid]+pls[i]<=c)u=mid;
				else v=mid-1;
			}
			ans+=u-(i-1)*(R[i]-L[i]+1);
		}
		if(a[R[q]] + pls[q] <= c)	ans += r - L[q] + 1;
		else if(a[L[q]] + pls[q] <= c)	for(int i = L[q]; i <= r; i++)	ans+=b[i] + pls[q] <= c;
	}
	return ans;
}

inline int getmax(int l, int r){
	int p = id[l], q = id[r];
	int ans = -0x3f3f3f3f;
	if(p == q){
		for(rg int i = l; i <= r; i++)	ans = max(ans, b[i]+pls[p]);
	}else{
		if(a[R[p]]+pls[p]>ans)for(rg int i = l; i <= R[p]; i++)	ans = max(ans, b[i]+pls[p]);
		for(rg int i = p+1; i <= q-1; i++)	ans = max(ans, a[R[i]]+pls[i]);
		if(a[R[q]]+pls[q]>ans)for(rg int i = L[q]; i <= r; i++)	ans = max(ans, b[i]+pls[q]);
	}
	return ans;
}

inline int getmin(int l, int r){
	int p = id[l], q = id[r];
	int ans = 0x3f3f3f3f;
	if(p == q){
		for(rg int i = l; i <= r; i++)	ans = min(ans, b[i]+pls[p]);
	}else{
		if(a[L[p]]+pls[p]<ans)for(rg int i = l; i <= R[p]; ++i)	ans = min(ans, b[i]+pls[p]);
		for(rg int i = p+1; i <= q-1; ++i)	ans = min(ans, a[L[i]]+pls[i]);
		if(a[L[q]]+pls[q]<ans)for(rg int i = L[q]; i <= r; ++i)	ans = min(ans, b[i]+pls[q]);
	}
	return ans;
}
int c[2007];
inline int ask(int dl, int dr, int k){
	if(dr-dl+1 < k or k < 1)	return -1;
	int l = getmin(dl, dr), r = getmax(dl, dr);
	if(id[dl] == id[dr]){
		for(int i = 0; i <= dr - dl; ++i){
			c[i] = b[i+dl];
		}
		std::sort(c, c+dr-dl+1);
		return c[k-1]+pls[id[dl]];
	}
	int ans = 0;
	while(l <= r){
		int mid = l+((r-l)>>1);
		int check = query(dl, dr, mid);
		if(check < k)	l = mid+1;
		else r = mid-1, ans = mid;
	}
	return ans;
}

signed main(){
	int n = read(), m = read();
	for(rg int i = 1; i <= n; ++i)	a[i] = read(), b[i] = a[i];
	int t(200);
	for(int i=1;i<=n;i++)id[i]=(i-1)/t+1;
	for(int i=1;i<=id[n];i++)L[i]=(i-1)*t+1,R[i]=i*t;
	R[id[n]]=n;
	for(rg int i = 1; i <= id[n]; ++i){
		std::sort(a+L[i], a+R[i]+1);
	}
	int op,l,r,k;
	while(m--){
		op = read(), l = read(), r = read(), k = read();
		if(op-1) add(l, r, k);
		else write(ask(l, r, k)), putchar('\n');
	}
	return 0;
} 

顺利卡入最优解第一页。

posted @ 2022-09-24 11:59  WRuperD  阅读(49)  评论(0编辑  收藏  举报

本文作者:DIVMonster

本文链接:https://www.cnblogs.com/guangzan/p/12886111.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

这是一条自定义内容

这是一条自定义内容