首页 > 其他分享 >洛谷 P8500 - [NOI2022] 冒泡排序

洛谷 P8500 - [NOI2022] 冒泡排序

时间:2023-08-07 14:34:14浏览次数:53  
标签:限制 argmin int 位置 冒泡排序 P8500 NOI2022 MAXN 区间

显然将权值离散化是没有问题的,因为必然存在一组最优解,满足每个 \(a_i\) 都取自于某个 \(V_i\),于是不管三七二十一先将 \(V_i\) 离散化了再说。

考虑从部分分入手逐步分析这道题:

  • 特殊性质 A:\(V_i=1\) 相当于这个区间中的数必须是 \(1\),先将这些数去掉不管,紧接着考虑 \(V_i=0\) 的限制。我们贪心地想,对于每个区间,既然必须要有至少一个数是 \(0\),那么我们肯定希望这个 \(0\) 越靠左越好,这样更有利于最小化逆序对个数。因此不难想到这样的策略:将所有 \(V_i=0\) 的区间按左端点从大到小排序,然后依次考虑每个区间,如果里面没有 \(0\) 就将最左边不是 \(1\) 的位置设成 \(0\)。如果找不到就 \(-1\)。这样还会有一些位置没有填,这些位置肯定一段前缀是 \(0\),剩下是 \(1\)。直接枚举这个位置然后 \(O(1)\) 算答案即可。
  • 特殊性质 B:限制相当于限制单点上的值。有限制的部分是容易计算的,考虑那些没有限制的部分,对于一个位置 \(i\),我们记 \(v_{i,x}\) 表示令 \(a_i=x\) 的情况下,那些有限制的点与 \(i\) 构成的逆序对个数,然后我们发现,\(v_i\) 的 argmin 随着 \(i\) 的增加是不断增大的,这个容易用找最优解的过程得出,这样我们直接取 argmin 就是答案,因为这样没有限制的部分内部的逆序对个数是 \(0\),也达到了最小。使用线段树维护之即可。
  • 特殊性质 C:显然,我们会将每个区间的最小值摆在最左边的问题。后面的情况与特殊性质 B 类似,只不过有一个额外条件就是有个 \(a_i\ge lim_i\) 的限制。这时候我们大胆猜个结论:还是套用特殊性质 B 的做法,不过加上这个限制之后我们只能在 \(\ge lim_i\) 的部分找 argmin,如果有多个相同的则取最小的。正确性我不太会证明,不过感性理解一下就是,如果你比 argmin 更大那肯定不优,因为对于后面的位置而言肯定 \(a_i\) 越小越不容易产生逆序对,而如果比 argmin 小,那么我们将 \(a_i\) 与最优的 argmin 之间的所有位置都变为 argmin 是更优的。这样我们同样用线段树维护即可。

正解实际上是 A 与 C 的结合:即我们从大到小枚举 \(x\),然后按左端点递减的顺序考虑所有 \(V_i=x\) 的区间,如果区间中没有 \(a_i=x\) 的位置就找到最左边没有被标记的位置,令其 \(a_i=x\),找不到就 \(-1\)。处理完了之后把这些区间中所有位置都标记了。然后套用 C 的做法即可。前一半可以使用并查集的经典套路处理。这样复杂度就是 \(O(n\log n)\)。

const int MAXN=1e6;
int n,m,key[MAXN+5],uni[MAXN+5],num,lw[MAXN+5];
struct dat{int l,r,v;}a[MAXN+5];
vector<pii>vec[MAXN+5];
vector<int>ins[MAXN+5],del[MAXN+5];
int f[MAXN+5],nd[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void clear(){
	num=0;
	for(int i=1;i<=n;i++)nd[i]=0;
	for(int i=1;i<=n+1;i++)ins[i].clear(),del[i].clear();
	for(int i=1;i<=m;i++)vec[i].clear();
	for(int i=1;i<=n+1;i++)f[i]=0;
}
struct node{int l,r,lz;pii mn;}s[MAXN*4+5];
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;s[k].lz=0;s[k].mn=mp(0,l);if(l==r)return;
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); 
}
void tag(int k,int v){s[k].lz+=v;s[k].mn.fi+=v;}
void pushdown(int k){if(s[k].lz)tag(k<<1,s[k].lz),tag(k<<1|1,s[k].lz),s[k].lz=0;}
void modify(int k,int l,int r,int v){
	if(l>r)return;if(l<=s[k].l&&s[k].r<=r)return tag(k,v),void();
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(r<=mid)modify(k<<1,l,r,v);else if(l>mid)modify(k<<1|1,l,r,v);
	else modify(k<<1,l,mid,v),modify(k<<1|1,mid+1,r,v);
	s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
}
pii query(int k,int l,int r){
	if(l<=s[k].l&&s[k].r<=r)return s[k].mn;pushdown(k);
	int mid=s[k].l+s[k].r>>1;
	if(r<=mid)return query(k<<1,l,r);else if(l>mid)return query(k<<1|1,l,r);
	else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
struct fenwick{
	int t[MAXN+5];
	void init(){for(int i=1;i<=num;i++)t[i]=0;}
	void add(int x,int v){for(int i=x;i<=num;i+=(i&(-i)))t[i]+=v;}
	int query(int x){int ret=0;for(int i=x;i;i&=(i-1))ret+=t[i];return ret;}
}T;
void solve(){
	scanf("%d%d",&n,&m);clear();
	for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v),key[i]=a[i].v;
	for(int i=1;i<=m;i++)key[i]=a[i].v;sort(key+1,key+m+1);key[0]=-1;
	for(int i=1;i<=m;i++)if(key[i]!=key[i-1])uni[++num]=key[i];
	for(int i=1;i<=m;i++)a[i].v=lower_bound(uni+1,uni+num+1,a[i].v)-uni;
	for(int i=1;i<=m;i++)vec[a[i].v].pb(mp(a[i].l,a[i].r));
	for(int i=1;i<=m;i++)ins[a[i].l].pb(a[i].v),del[a[i].r+1].pb(a[i].v);
	multiset<int>st;
	for(int i=1;i<=n;i++){
		for(int x:ins[i])st.insert(x);
		for(int x:del[i])st.erase(st.find(x));
		lw[i]=(st.empty())?1:(*st.rbegin());
	}
	for(int i=num;i;i--){
		sort(vec[i].begin(),vec[i].end(),[&](pii p1,pii p2){return p1.fi>p2.fi;});
		int lst=0;
		for(pii p:vec[i]){
			if(p.fi<=lst&&lst<=p.se)continue;
			int pos=find(p.fi);
			if(pos>p.se)return puts("-1"),void();
			nd[pos]=i;lst=pos;
		}
		for(pii p:vec[i]){
			while(1){
				int x=find(p.fi);
				if(x>p.se)break;f[x]=x+1;
			}
		}
	}
	build(1,1,num);
	for(int i=1;i<=n;i++)if(nd[i])modify(1,nd[i]+1,num,1);
//	for(int i=1;i<=n;i++)printf("%d%c",nd[i]," \n"[i==n]);
	for(int i=1;i<=n;i++){
		if(nd[i])modify(1,nd[i]+1,num,-1),modify(1,1,nd[i]-1,1);
		else{
			int pos=query(1,lw[i],num).se;
			nd[i]=pos;modify(1,1,nd[i]-1,1);
		}
	}
	T.init();ll res=0;
	for(int i=1;i<=n;i++)res+=i-1-T.query(nd[i]),T.add(nd[i],1);
	printf("%lld\n",res);
}
int main(){
	int qu;scanf("%d",&qu);
	while(qu--)solve();
	return 0;
}

标签:限制,argmin,int,位置,冒泡排序,P8500,NOI2022,MAXN,区间
From: https://www.cnblogs.com/tzcwk/p/luogu-P8500.html

相关文章

  • 冒泡排序
    defbubble_sort(nums):  n=len(nums)  foriinrange(n-1):    forjinrange(n-i-1):      ifnums[j]>nums[j+1]:        nums[j],nums[j+1]=nums[j+1],nums[j]nums=[5,3,8,2,1]print("排序前:",nums)bubble_sort(......
  • 冒泡排序原理推导
    与前一项比大小arr=[4,3,2,1]n=len(arr)foriinrange(0,n-1):#如果n=0,1;range输出空表格,不进行for循环print('第{}遍'.format(i+1))forjinrange(1,n-i):ifarr[j-1]>arr[j]:arr[j-1],arr[j]=arr[j],arr[j-1]arr与后一项......
  • 算法-06-冒泡排序
       importrandomdefbubble_sort(li):foriinrange(len(li)-1):forjinrange(len(li)-i-1):ifli[j]>li[j+1]:li[j],li[j+1]=li[j+1],li[j]li=[random.randint(0,20)foriinrange(15)......
  • 冒泡排序
    第一趟:相邻比较,若前>后,交换位置,直到最后一个位置为max第二趟:相邻比较,若前>后,交换位置,直到倒数第二个位置为max(除最后一个位置)第n趟:......@Testpublicvoidtest1(){int[]arr={7,6,5,4,3,2,1,1};inttemp;//比较趟数。共leng......
  • java常见的排序算法(冒泡排序、选择排序、插入排序、shell排序、归并排序、堆排序、快
    文章目录一、冒泡排序1、效率表现和适用范围2、算法实现二、选择排序1、效率表现和适用范围2、算法实现三、插入排序1、效率表现和适用范围2、算法实现四、shell排序1、效率表现和适用范围2、算法实现五、归并排序1、效率表现和适用范围2、算法实现六、快速排序1、效率表现和适用......
  • C#冒泡排序算法
    冒泡排序实现原理冒泡排序是一种简单的排序算法,其原理如下:从待排序的数组的第一个元素开始,依次比较相邻的两个元素。如果前面的元素大于后面的元素(升序排序),则交换这两个元素的位置,使较大的元素“冒泡”到右侧。继续比较下一对相邻元素,重复步骤2,直到遍历到数组的倒数第二......
  • 冒泡排序
    #include<stdio.h>voidbubble_sort(intarr[],intsz){ inti=0; for(i=0;i<sz-1;i++) { intj=0; for(j=0;j<sz-1-i;j++) { if(arr[j]>arr[j+1]) { inttmp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=tmp; } } }}intmain()......
  • P8859 冒泡排序
    我回来了。参考:https://www.luogu.com.cn/blog/_post/509374、https://www.luogu.com.cn/blog/_post/510710。考虑type1,注意到\(1\)是不能被超越的,且一个数操作多次不优,因此第一步操作\(1\)不劣。因此从小到大归位每个数不劣,答案即为总数减去前缀\(\max\)的数目。从小到......
  • 2023.7.19 周三:冒泡排序
    1importjava.sql.SQLOutput;2importjava.util.Arrays;3importjava.util.Scanner;4//冒泡排序5publicclasstest{6publicstaticvoidmain(String[]args){7int[]a={5,4,6,8,9,1,7,2,3};8intarray[]=sort(a);9S......
  • 一维数组之冒泡排序
    从b站上黑马程序员的C++课里学到的冒泡排序 1#include<iostream>2usingnamespacestd;3intmain()4{5intarr[6]={2,4,1,6,7,3};6for(inti=0;i<6;i++)//数组遍历7{8cout<<arr[i]<<"";9}1......