Ultra-QuickSort OpenJ_Bailian-2299逆順序対+ツリー配列+離散化(構造を並べ替えることで実現)

トピックの主なアイデア:配列シーケンスを入力し、シーケンス全体をバブルソートするように要求する場合、各要素を交換する必要がある合計回数。

入る:

入力にはいくつかのテストケースが含まれています。各テストケースは、単一の整数n <500,000(入力シーケンスの長さ)を含む行で始まります。次のn行のそれぞれには整数が含まれます。つまり、0≤a[i]≤999,999,999であり、これはi番目の入力シーケンス要素です。入力は、長さn = 0のシーケンスで終了します。このシーケンスは処理しないでください。

出力:

プログラムは、入力シーケンスごとに、整数opを含む1行を出力します。ここで、opは、指定された入力シーケンスをバブルソートするために必要な交換操作の最小数です。

アイデア:


入力データの量は5e5以内であるため、データの量は問題ではありませんが、直接処理時間の複雑さが大幅に増加する場合(ツリー配列がロービットを使用するため)、値が99億と大きすぎることが問題です。操作)、したがって、データは離散化する必要があります。

いわゆる離散化(ビッグガイスキップ)は、オントロジーでは、各値の相対的な大きさを変更せず、新しい値のセットをそれらに割り当てることとして理解できます。

例:30 20 10 40 5060並べ替え後のこのデータセットは1020 30 40 5060です。

ソート後に値を32 1 4 5 6に変更すると、1 2 3 4 56になります。

上記の操作により、さまざまな入力位置が再ソートされ(ソートは各値の相対サイズにのみ依存します)、それらのソート交換時間が同じであることがわかるため、最初のデータセットを2番目のセットに離散化できます。データの、これは時間の複雑さを大幅に最適化することができます。


入力データは、主にval(値)とid(添え字シリアル番号)を格納する構造体に格納されます。これは離散化する必要があり、このアイデアは多くの場所で使用されるため、書き留める必要があります。下付き文字を保存する主な目的は、値を並べ替えるときに前の位置を記録し、前の位置に従って新しい値に離散化することです。詳細については、コードを参照してください。


この問題を解決するアイデアについて話しましょう。これは、バブルソートが交換される回数であるためです(昇順であると想定しています)。別の観点から問題を検討してください。逆の数がわかっている限り、問題が見つかります。ペアはシーケンス全体にあり、交換する必要がある回数がわかります。これは、反転したペアのすべてのペアを交換する必要があるためです。

このシーケンスの逆のペアを見つけるために、ツリー配列を使用して、各値の前にそれよりも小さい数を格納します。昇順であるため、各数値がそれよりも小さい数値がいくつあるかを後ろから前に確認する必要があります。確認するときは、離散化された値に従い、数値軸上にツリー配列を維持します。 getNumは、xより小さい数の場合、後ろから前に数を見つけるたびに、その数に1加算してツリー配列に入れ(これは非常に重要なアイデアです)、次に追加するときにそれ、あなたは答えの正しさを保証することができます。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 5e5 + 5;
typedef long long ll;
ll a[maxn], C[maxn << 1];
//0≤a [i]≤999,999,999  数据太大要需要进行离散化 
int n;
struct N{
	ll v;
	int id;
	bool operator < (const N & w)const{
		return v < w.v;
	}
}no[maxn];

void update(int x, int v){
	for(int i = x;i <= n;i += i & (-i)){
		C[i] += v;
	}
}

int getNum(int x){
	int ans = 0;
	for(int i = x;i > 0;i -= i & (-i)){
		ans += C[i];
	}
	return ans; 
}

int main(){
	while(scanf("%d",&n), n){
		memset(C, 0, sizeof C);
		for(int i = 1;i <= n;i++){
			scanf("%lld", &no[i].v);
			no[i].id = i;
		}
		sort(no + 1, no + n + 1);
		//更新a数组 
		for(int i = 1;i <= n;i++) {
			a[no[i].id] = i;
		}
		ll ans = 0;
		for(int i = n; i >= 1;i--){
			ans += getNum(a[i] - 1);
			update(a[i], 1);
		}
		printf("%lld\n",ans);	
	}
	
	return 0;
}

 

おすすめ

転載: blog.csdn.net/qq_40596572/article/details/104007691