首页 > 其他分享 >P9474 [yLOI2022] 长安幻世绘

P9474 [yLOI2022] 长安幻世绘

时间:2023-10-09 21:23:24浏览次数:34  
标签:数字 最大值 yLOI2022 幻世绘 最小值 区间 lsum ll P9474

题目意思:

需要在元素互不相同的数列 \(a\) 中选出一个长度为 \(m\) 的元素互不相邻的子列,使得子列的极差最小。

做法

我们知道,对于一组数列,我们只需知道它的最大值和最小值,就可以得到它的极差。那么我们可以将数字从小到大排序,固定最小值,寻找最优的最大值,当最小值和最大值的位置固定了,那么我们要选出的数定在最小值和最大值位置的范围内。

我们从小到大枚举最小值,容易发现最大值在已排序序列中的位置是有单调性的。便再用一个指针去枚举最大值。用线段树维护这个区间所有数的位置,对于一段连续位置的数字,最优的选取方案就是它的长度除以二(向上取整),那么整个贡献便是每段数字贡献之和。时间复杂度为 \(O(NlogN)\)。

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
ll n,m,res,ans=99999999999999;
struct xs{ll x,k;}a[N];
struct ys{
	ll l,r,sum,lsum,rs,rsum,ls,fla;
	//l表示区间内最左边可选数字的位置,r类似,表示最右边
	//lsum表示区间内最左边可选数字的所在段中最多能选的数字个数,lsum类似,表示最右边
	//rs表示区间内最左边可选数字的所在段中的右端点,ls类似,表示最右边的左端点
	//fla表示整个区间是否有且仅有一段可选数字,sum表示整个区间最多能选的数字个数 
}tr[N*4];
bool cmp(xs x,xs y){return x.x<y.x;}
void pushup(ll rt){
	if(tr[rt<<1].sum&&tr[rt<<1|1].sum){
		tr[rt].l=tr[rt<<1].l,tr[rt].r=tr[rt<<1|1].r;
		tr[rt].lsum=tr[rt<<1].lsum,tr[rt].rs=tr[rt<<1].rs,tr[rt].rsum=tr[rt<<1|1].rsum,tr[rt].ls=tr[rt<<1|1].ls; 
		if(tr[rt<<1].r==tr[rt<<1|1].l-1){
			if(tr[rt<<1].fla==1&&tr[rt<<1|1].fla==1){
				tr[rt].sum=tr[rt].lsum=tr[rt].rsum=(tr[rt<<1|1].r-tr[rt<<1].l+2)/2;
				tr[rt].fla=1,tr[rt].ls=tr[rt<<1].l,tr[rt].rs=tr[rt<<1|1].r;
			}else{
				tr[rt].sum=tr[rt<<1].sum-tr[rt<<1].rsum+tr[rt<<1|1].sum-tr[rt<<1|1].lsum+(tr[rt<<1|1].rs-tr[rt<<1].ls+2)/2;
				tr[rt].fla=0;
				if(tr[rt<<1].fla){
					tr[rt].lsum=(tr[rt<<1|1].rs-tr[rt<<1].l+2)/2,tr[rt].rs=tr[rt<<1|1].rs;
					tr[rt].rsum=tr[rt<<1|1].rsum,tr[rt].ls=tr[rt<<1|1].ls;
				}
				if(tr[rt<<1|1].fla){
					tr[rt].rsum=(tr[rt<<1|1].r-tr[rt<<1].ls+2)/2,tr[rt].ls=tr[rt<<1].ls;
					tr[rt].lsum=tr[rt<<1].lsum,tr[rt].rs=tr[rt<<1].rs;
				}
			}
		}else tr[rt].fla=0,tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
	}else{
		tr[rt].sum=tr[rt].lsum=tr[rt].sum=tr[rt].fla=tr[rt].l=tr[rt].r=tr[rt].ls=tr[rt].rs=0;
		if(tr[rt<<1].sum){
			tr[rt].sum=tr[rt<<1].sum,tr[rt].lsum=tr[rt<<1].lsum,tr[rt].rsum=tr[rt<<1].rsum,tr[rt].fla=tr[rt<<1].fla;
			tr[rt].l=tr[rt<<1].l,tr[rt].r=tr[rt<<1].r,tr[rt].ls=tr[rt<<1].ls,tr[rt].rs=tr[rt<<1].rs;
		}
		if(tr[rt<<1|1].sum){
			tr[rt].sum=tr[rt<<1|1].sum,tr[rt].lsum=tr[rt<<1|1].lsum,tr[rt].rsum=tr[rt<<1|1].rsum,tr[rt].fla=tr[rt<<1|1].fla;
			tr[rt].l=tr[rt<<1|1].l,tr[rt].r=tr[rt<<1|1].r,tr[rt].ls=tr[rt<<1|1].ls,tr[rt].rs=tr[rt<<1|1].rs;
		}
	}
	
}
void add(ll rt,ll l,ll r,ll x,ll d){
	if(l==r){
		tr[rt].fla=d;
		if(d)tr[rt].l=tr[rt].r=tr[rt].ls=tr[rt].rs=x,tr[rt].sum=tr[rt].lsum=tr[rt].rsum=1;
		else tr[rt].l=tr[rt].r=tr[rt].ls=tr[rt].rs=0,tr[rt].sum=tr[rt].lsum=tr[rt].rsum=0;
		return;
	}
	ll mid=(r+l)>>1;
	if(x<=mid)add(rt<<1,l,mid,x,d);
	else add(rt<<1|1,mid+1,r,x,d);
	pushup(rt);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i].x,a[i].k=i;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=m;i++)add(1,1,n,a[i].k,1);
	for(int i=1,p=m;i<=n-m+1;i++){								//固定最小值 
		while(tr[1].sum<m&&p<n)add(1,1,n,a[++p].k,1);			//不断增大最大值
		if(tr[1].sum>=m)ans=min(ans,a[p].x-a[i].x);				//tr[1].sum包含了整个区间的最优选择,直接判断其是否能取到 m 个 
		add(1,1,n,a[i].k,0);									//把没用的的数字去除,维护线段树的正确性 
	}
	cout<<ans;
}

标签:数字,最大值,yLOI2022,幻世绘,最小值,区间,lsum,ll,P9474
From: https://www.cnblogs.com/Exotic-sum/p/17753179.html

相关文章

  • P9474 [yLOI2022] 长安幻世绘
    题目大意在元素互不相同的数列\(a\)中选出一个长度为\(m\)的元素互不相邻的子列,使得子列的极差最小。思路爆搜、\(dp\)肯定是过不了的,所以我们考虑固定某个值,赛时想到了固定最大或者最小值,然后找到另一个值,但是除了\(dp\)没想到好做法,比赛结束了才知道正解居然是同时固......
  • LGR-147-Div.3】洛谷网校 7 月普及组月赛 & yLOI2022 总结
    Upd:2023/8/5补T1普及组的题,而且T1,而且叫签到题。所以非常简单,入门难度。没什么好说的。就是统计大写,小写和字母个数。#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintN=100+5;strings;intmain(){ cin>>s; intx=0,y=0,z=0; for(inti=......
  • luogu P9474 [yLOI2022] 长安幻世绘 详细题解
    原题:P9474[yLOI2022]长安幻世绘看到很多大佬的题解直接讲了做法,本蒟蒻看得不是很懂,调了很久才把这题做出来,于是写了这篇比较详细的题解谈一下我做这题从头到尾的思路,希望对各位有帮助qwq。思路我们首先思考这样一个问题,如果已经知道最终答案对应的最大值和最小值,又不知道题......
  • 题解 P9474 [yLOI2022] 长安幻世绘
    看到极差,不难想到双指针。显然,如果\([l,r]\)的位置都被覆盖,那么其中最多可以选\(\lceil\frac{r-l+1}{2}\rceil\)个数。我们先将所有数离散化,排序,双指针卡取值范围。set里面存pair类型变量,表示覆盖的区间。每次将值为\(r\)的数的位置加入,在set中二分到与它相邻的区......