Loj#2719「NOI2018」バブルソート

Loj#2719「NOI2018」バブルソート

タイトル説明

最近では、小型のSバブルソートは強い関心を生成しています。簡単な問題、研究だけ小さなSのために*の\(1 \)\(\ N-) *の配置されたバブルソートを。

以下は、バブルソートアルゴリズムの説明です。

入力:N pの配列の長さ[1 ... N]

出力:ソートpの結果。

んnとI = 1のために

nまでJ = 1 - 1行います

もし(P [J]> P [J + 1])

取引値p [J]とp [J + 1]の

交流バブルソートの数は、交換処理の実行回数として定義されます。交換の数の下限であることを証明することができます- \(P_I \ rvert \ \ FRAC 1 2 \ sum_ {I = 1} ^ N- \ lvert I) \(P_I \)が配置されている\(P \)最初の\(私は)\デジタルポジションを。あなたが証拠に興味がある場合は、ヒントを見ることができます。

小Sは、長さの研究に焦点を当てるようになった(\ N-)\交換の数を満たすために配置において(= \ FRAC 1 2 \ \ - P_I \ rvert \ sum_ {i = 1} ^ N \ lvert I) 整列(後論文では、便宜上、我々は)である「良い」と呼ばれる、すべてのこのような配置の配置を置きます。さらに彼は、最後の配置よりも、そんなに多く思いましたか?密度の高い彼らはあまり密に分布していますか?

Sは、一定の長さのために小さなたい\(N- \)に配置\(Q \)を辞書順厳密に大きいより算出した、\(Q \)「良い」配置の数を。しかし、彼が行う、とあなたに入らない、私はあなたが彼の答えは非常に大きくなる可能性を考慮に入れて、この問題を解決を願って、その出力に唯一の答え(998 244 353 \)\結果モジュロ。

入力形式

ファイルからinverse.inデータを読み込みます。

最初の行の入力は正の整数含ま\(T \)を、データセットの数を表します。

各試験のために、最初の行は、正の整数有する\(\ N-)を確実にするために、\を(N- \ 1当量6 \ 10回5 ^ \)

次の行を入力する\(N- \)に記載のタイトルに対応する正の整数であり、\(Q_I \) 入力されたことを確認\(1 \)\(N- \)配置。

出力フォーマット

ファイルへの出力inverse.outインチ

出力コモン\(T \)ライン、各ライン整数。

辞書配列よりも厳密に大きい各試験、出力整数を\(Q \)は、「良好な」ペアの数に配置された\(998244353 \)モジュロをもたらします。

データ範囲とヒント

\(N \の当量600000、\合計N \の当量2000000 \)


合法的なシーケンスは、その後、ほとんどの任意の数のために片側に移動した場合、それは一種のみバブルの過程にあり、見つけることができます。私たちは、その数よりも多いがある場合と同じように、左、右はその数よりも少ない数を持っている場合、それは右に移動します、知っておく必要があり、その後、左に移動します。したがって、各シーケンスの法的要件の数は逆の右の両側にすることはできません。

この原則によれば、のプレスのようなアプローチを取得することは容易です。

そして、多項式の複雑さの練習があるかどうかを検討してください。左から右へと塗りの数を考えてみましょう。最大数に充填されているものとする(MXを\)\、その後、数は比に充填されていない\(のMX \)ので、そのごく一部左を逆にされている、あなたは右側に逆を持つことができませんこのスコアは、昇順で保持さが、より多くする必要があります\(MX \)の主要な部分は制限はありません。

だから、できる(DP \)\ Aを。セット\(F_ {I、Jは} \) 左の表す\(Iは\)番号が充填されていない、の数に制限はありません(J \)\プログラムの数。

初期化:\(F_ = {0,0} 1 \。)

転送:
\ [I {F_、F_ {J} = 1-I、J} + \ sum_ ^ {K} = 0 {J-F_ {} 1 1-I、K} \\ = \ sum_ {K = 0 } ^ jf_ {I-1、
K} \]がない場合は、その後、彼はいくつかの数に制限はありませんことを知っていることはどのように多くのより多くの数に制限の残りの部分を知ることができ、新しいミックスに追加の数が限られているかどうかを検討しますA。

この\(DP \)式は、(O(N ^ 3)\ \) 、私たちは以下の形式の方程式を書くことができる:
\ [\開始{左=整列} F_ {I、J}&= F_ {I、 J-1} + F_ {I
-1、J} \\ F_ {I、0}&= 1 \端{\]}整列これはなる\(O(N ^ 2) \) の。

次に考える\(F_ {N、M} \) からグリッド、:意味の組み合わせ\((0,0)\)来る\((N、M)\) 各昇圧のみすぐに、と\(私はGEQのj個の\を\)プログラムの数。その全体のプロセスが経過していないことができ\(Y = X + 1 \ ) この行を。

次に、あなたが解決するために、割引定理と呼ばれるものを使用することができます。取得\((0,0)\)\(Y = X + 1 \ ) 対称点- (1,1)\()\を次にから決定し、(\(1,1)) - \行きます((N、M)\) \ プログラムの数。\(( - 1,1)\)する\((N、M)\ ) 各パスが経過しなければならない\(Y = X + 1 \ ) 、パスに沿って(不正の場合に特有の元の問題\ (Y = X + 1 \)が)折り畳ま。従って:
\ [N-F_ {、}、M = \ Binom {M} + {N-M} - \ Binom {N-M} + {} 1-M \]は、
辞書式順序を扱う考えます。私たちは、列挙する\(k個\)を、それが表し\(k個\)全てが前の位置に関連している\(Q_I \)と同じ、その後、\(k個\) このポジションを埋めるために数\(> q_kを\) 仮定(Q_ {1 \ ldots K \ } \) 最大で\(Q_ {MX} \)で、\(Q_ {MX + 1 \ N ldots} \) より\(Q_ {MX} \)ラージそこ\(CNT \) Aは、この時点で答えが\(F_ {N-I + 1、CNT-1} \)

ポイントは、我々はこれである理由について話しています。私たちは、セット\(K \)最大は前だった\(MX \)

  1. \(q_k> MX \) \(k個の\)この位置にのみよりも充填することができる(q_k \)\多く、それはより明白である(MXの\)\大きな数なので、これらの数は限定されるものではありません。列挙\(K \)は、この位置は、いくつかの数字を埋めるために最初に、答えは\ sum_ {J = 0(\ } ^ {CNT-1} F_ {NI、J} =のF_ {NI + 1 、1-CNT} \)
  2. \(q_k <MX \) 仮定すると、\(K \)番号がこの位置を埋めることは\(q_k <X <MX \ ) 、明らかに不可能です。以降\(X \)がある\(q_k \)すべての制限された数である、唯一の上昇を維持するので、\(X <q_k <MX \) のみ充填することができる\(> MX \)番号を、答えはまだ\(\ sum_ {J = 0 } ^ {CNT-1} F_ {NI、J} =のF_ {NI + 1、CNT-1} \) 。

シーケンスは、正当な両端を持っていない場合。

具体的な実装では、フェンウィックツリーを使用することができます。

コード:

#include<bits/stdc++.h>
#define ll long long
#define N 600005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

const ll mod=998244353;
ll ksm(ll t,ll x) {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}
int n;
int q[N];
ll fac[N*2],ifac[N*2];
void pre(int n) {
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    ifac[n]=ksm(fac[n],mod-2);
    for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
}

ll C(int n,int m) {return fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
ll cal(int n,int k) {
    if(k==0) return 1;
    if(k==1) return n;
    return (C(n+k,k)-C(n+k,k-1)+mod)%mod;
}
int low(int i) {return i&(-i);}
int tem[N];
void add(int v,int f) {for(int i=v;i<=n;i+=low(i)) tem[i]+=f;}
int query(int v) {
    int ans=0;
    for(int i=v;i;i-=low(i)) ans+=tem[i];
    return ans;
}

bool vis[N];
int main() {
    pre(1200000);
    int T=Get();
    while(T--) {
        ll ans=0;
        n=Get();
        for(int i=1;i<=n;i++) tem[i]=0;
        for(int i=1;i<=n;i++) q[i]=Get();
        int mx=0,cnt; 
        for(int i=1;i<=n;i++) {
            if(q[i]>mx) {
                mx=q[i];
                cnt=n-q[i]-(i-1-query(q[i]-1));
            }
            (ans+=cal(n-i+1,cnt-1))%=mod;
            int low=query(q[i]-1);
            if(low!=i-1&&low!=q[i]-1) break;
            add(q[i],1);
        }
        cout<<ans<<"\n";
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/hchhch233/p/10963665.html