【問題】整理する

タイトル

テストリンク:いいえ。

質問の出典:デモT2。

トピックの背景

リトルGはアレンジをするのが好きです。

タイトルの説明

現在、彼には2つの\(n \)配置があります\(N- \)によって配置(0,1,2、\ cdots、N \ -1 \) この\(N- \)番目の桁。配置\(p \)の場合、\(\ operatorname {Order}(p)\)は、\(p \)が辞書式順序\(\ operatorname {Order}(p)\)小さい配置(\( from 0 \)カウントを開始します)。\(n!\)\(X \)未満の負でない数値の場合\(\ operatorname {Perm}(x)\)最小の辞書式順序\(x \)を意味します。

ここで、Gは手元にある2つの順列を合計したいと考えています。2つの配置\(p \)\(q \)の合計は、\(sum = \ operatorname {Perm}((\ operatorname {Order}(p)+ \ operatorname {Order}(q))\ bmod {n !})\)

入力フォーマット

入力ファイルの最初の行に番号\(n \)を入力します。意味はタイトルです。

次の2行で、各行のスペースで区切られた\(n \)番号は、手元にある小さなGの2つの配置を示しています。

出力フォーマット

2つの配置の合計を示す、スペースで区切られた\(n \)番号の行を出力します。

評価限界とデータ範囲

評価期限\(1000 \ \ textrm {ms} \)、スペース制限\(128 \ \ textrm {MiB} \)

  • \(40 \%\)のデータについては、\(1 \ le n \ le 10 \) ;
  • 別の\(30 \%\)のデータの場合、2番目の配置\(q \)\(\ operatorname {Order}(q)\ le 10 ^ 5 \)を満たすことを確認してください
  • 以下のための\(100 \%\)データ、\(1 \ N-LE \ル。5 \ ^ 10で3回\)

分析

この質問のデータ範囲は非常に混乱しています。この問題を回避するには、数学的な基礎が少し必要になる場合があります。

\(40 \ \ texttt {pts} \)

このファイルの特徴は\(n!\)比較的小さいため、これを使用してCantor展開を行うことができます。

タイトルが言うように、最初の拡張の後に追加を行います。

複雑さ\(\シータ(n!)\)

その他\(30 \ \ texttt {pts} \)

カントールの一つは、拡張することができ、もう一方が行うには、それを拡大する結果を使用してnext_permutationすることができます。

複雑さ:\(\ Theta(\ operatorname {Order}(q))\)

\(100 \ \ texttt {pts} \)

この問題の難しさは、直接完全に開発することができないことであることに注意してください。そうしないと、計算がより困難になります。

では、Cantorの展開の一部を維持し、直接高精度の計算を実行できるでしょうか。ええ

関東の展開を理解しましょう:

  1. 順序付けられた辞書式順序は、式によって決定できます。

\ [\ huge {\ sum \ limits_ {i = 0} ^ {n-1} i!\ times p_i \ text {ranking in the残りの部分}} \]

  1. 配置は、\(\左<n、n-1、n-2、\ ldots、2、1 \右> \) 16進数に対応します(これを\(\ operatorname {Order2}と呼びましょう)(p) \))、つまり、最初の\(i \)はフル\(i \)です。

ちょうど\(p = \ left <1,4,2,3 \ right> \)\(\ left <0,2,0,0 \ right> \)に対応します。この番号は\(\ operatorname {Order}(p)\)に対応します。

そのノート(\ operatorname {Order2}(\ \ P))は、高精度の数であるので、我々はそれを直接それらを加算および減算することはできませんか?答えはイエスです。これは積極的な解決策です。

\(\ mathcal {E} xtra \スコア\)

もちろん、\(n \ le 5000 \)はこの質問の制限ではありません。この質問のデータを引き続き取得できます。

なぜ複雑なのですか\(\ Theta(n ^ 2)\)それは、Cantorの展開が非常に遅いためです。ランキングが再計算されるたびに、再割り当てされます。

そこにコメントしてください:

for (int i = 0; i < n; i++)
	rk[i] = i;

for (int i = 0; i < n; i++)
{
	ptr[i] = rk[tmp];
	
	for (int j = tmp + 1; j < n; j++) // Here!!!!!!!
		rk[j]--;
}

明らかに、ラインセグメントツリーを使用して最適化できます。これにより、複雑さをさらに\(\ Theta(n \ log n)\)に減らすことができますもちろん、これは必要ないかもしれません(要するに、より癌性になります)

コード

複雑さの一般的な方法は\(\ Theta(n ^ 2)\)です。興味がある場合は、ラインツリーの最適化を試すことができます。

#include <cstdio>
#include <cstring>
using namespace std;

const int max_n = 5000;

int na[max_n], nb[max_n], rk[max_n];

void make_num(int n, int *ptr)
{
	int tmp;
	
	for (int i = 0; i < n; i++)
		rk[i] = i;
	
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &tmp);
		ptr[i] = rk[tmp];
		
		for (int j = tmp + 1; j < n; j++)
			rk[j]--;
	}
}

int main()
{
	int n, car = 0;
	
	scanf("%d", &n);
	
	make_num(n, na);
	make_num(n, nb);
	
	for (int i = n - 1; i >= 0; i--)
	{
		na[i] += nb[i] + car;
		
		if (na[i] >= n - i)
			na[i] -= n - i, car = 1;
		else
			car = 0;
	}
	
	for (int i = 0; i < n; i++)
		rk[i] = i;
	
	for (int i = 0; i < n; i++)
	{
		printf("%d ", rk[na[i]]);
		
		for (int j = na[i] + 1; j < n; j++)
			rk[j-1] = rk[j];
	}
	putchar('\n');
	
	return 0;
}

追記

この質問は興味深いもので、アレンジメントの問題を高精度のテンプレートに変えます。可能であれば、ラインツリーの最適化を追加できますが、多くの人が不快に感じるでしょう。

実際、線分を追加しなかった人がなぜなのかを考える必要があります。実際、それはトピックの継続性のためです。

良い質問は1つにまとめるべきではありませんが、一回限りの気持ちでなければなりません。そうでなければ、誰もこの質問をする気はありません。

おすすめ

転載: www.cnblogs.com/5ab-juruo/p/solution-20200411-perm.html