第k小数 & 逆序对

第k小数

注意细节即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<queue>
#include<iomanip>

using namespace std;
int n,k,ans;
int a[5000010];
long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}

void q_sort(int l,int r){
	if(l==r){
		ans=a[l];
		return ;
	}
	int mid=a[(l+r)>>1];
	int i=l,j=r;
	do {
		while(a[i]<mid&&i<=j)i++;  // detail 1
		while(a[j]>mid&&i<=j)j--;  // detail 2
		if(i<=j){    // detail 3
			swap(a[i],a[j]);
			i++,j--;
		}
	}while(i<=j);    // detail 4
//	cout<<i<<","<<j<<":"; 
//	for(int i=1;i<=n;i++){
//		cout<<a[i]<<" ";
//	}
//	cout<<endl;
	if(k==j+1){    // detail 5
		ans=a[j+1];
		return ;
	}
	else if(k<=j){   // detail 6
		q_sort(l,j);
	}
	else {
		q_sort(i,r);     // detail 7
	}
}

int main(){
//	freopen("","r",stdin);
//	freopen("","w",stdout);
	n=read();k=read()+1;
	for(int i=1;i<=n;i++)a[i]=read();
	
	q_sort(1,n);
	cout<<ans<<endl;
	return 0;
}

注意细节!!!

或者使用函数:nth_element(a+start,a+question,a+end);
结果为:a[question]

Example:

int a[50]={0,1,2,3,4,5,6,7,8,9,10};
nth_element(a+1,a+10,a+11);
cout<<a[10]<<endl;

注意!此函数会把数组顺序打乱!

逆序对

方法一:归并排序,思想为:若左半部分存在一个数大于右半部分的一个数,那么,从此数到mid,均大于右边的那个数,累计答案

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>

using namespace std;
long long n,a[500010],ans;
long long c[500010],tot;
long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+(long long)(ch-'0');ch=getchar();}
	return x*h;
}

void m_sort(int l,int r){
	if(l==r)return ;
	int mid=(l+r)>>1;
	m_sort(l,mid);m_sort(mid+1,r);
	tot=0;
	int i,j;
	for(i=l,j=mid+1;i<=mid&&j<=r;){
		if(a[i]<=a[j])c[++tot]=a[i++];
		else {
			c[++tot]=a[j++];
			ans+=(mid-i+1);  //核心部分
		}
	}
	while(i<=mid)c[++tot]=a[i++];
	while(j<=r)c[++tot]=a[j++];
	for(i=l,j=1;i<=r;i++,j++)a[i]=c[j];
	return ;
}

int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
//	q_sort(1,n);
	m_sort(1,n);
	cout<<ans<<endl;
	return 0;
}

记住,在统计逆序对时,必须使用long long,以防爆炸

时间 n log n

方法二:树状数组

离散化,然后按输入顺序依次插入,插入后在此数后面的数字必然与此数构成逆序对,统计个数。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<iomanip>

using namespace std;
long long n,ans,t[500010],tr[500010];
struct qwe{
	long long val,id;
}a[500010];
long long read(){
	long long x=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+(long long)(ch-'0');ch=getchar();}
	return x*h;
}

long long lowbit(long long x){
	return x&(-x);
}
bool cmp(qwe a,qwe b){
	if(a.val==b.val)return a.id<b.id;   //如果大小一致,一定要按照输入顺序排
	else return a.val<b.val;
}

void add(long long pos,long long val){
	for(long long i=pos;i<=n;i+=lowbit(i)){
		tr[i]+=val;
	}
	return ;
}

long long query(long long pos){
	long long k=0;
	for(long long i=pos;i;i-=lowbit(i)){
		k+=tr[i];
	}
	return k;
}

int main(){
	n=read();
	for(long long i=1;i<=n;i++)a[i].val=read(),a[i].id=i;
	
	sort(a+1,a+n+1,cmp);
	
	for(long long i=1;i<=n;i++){
		t[a[i].id]=i;
	}
	
	for(long long i=1;i<=n;i++){  //核心部分
		add(t[i],1);  //插入这个数
		long long x=query(t[i]);   //查询这个数之前有多少个数字(包含此数)
		ans+=(i-x);   //总数减前面的数即可
	}
	cout<<ans<<endl;
	return 0;
}

posted @ 2021-10-21 09:28  Charisk_FOD  阅读(39)  评论(0)    收藏  举报