No.5012 Better Mo's Algorithm is Needed!! (Execute)
タグ : / 解いたユーザー数 7
作問者 :
おことわり
この問題は、それなりに競技性に欠くスコア問題である可能性があります。どうか、あまりムキにならないでください。
同一の 出力 / 提出 に対してスコアが変動しますが、ジャッジがそこそこ重いため過度な同一提出はお控えください。
問題文
実行時間制限が通常より短い ms であることに注意してください。
また、非常に特殊な採点方法に注意してください。
スコア問題であるため、最後の提出から5分間は次の提出を行うことができません。
ここに、以下のような問題があります。この問題は Static Range Inversions Query としてよく知られています。
数列 が与えられます。 以下の 個の問い合わせに答えてください:
- 番目の問い合わせでは が与えられる。連続部分列 の転倒数を求めよ。
物理好きさんは の順列である さえうまく定めれば先程の問題に正解する 以下のコード を書きました。
#include <bits/stdc++.h> #include <sys/time.h> using namespace std; long long start_time; void start_clock(){ struct timeval tv; gettimeofday(&tv, NULL); start_time=(tv.tv_sec*1000000+tv.tv_usec); } long long current_clock(){ struct timeval tv; gettimeofday(&tv, NULL); long long current_time=(tv.tv_sec*1000000+tv.tv_usec); // cout << current_time-start_time << "(us)\n"; return current_time-start_time; } int bit[111111]={0}; int bsize; int BIT_sum(int i){ int s=0; while(i>0){ s+=bit[i]; i-=i&(-i); } return s; } void BIT_add(int i,int x){ while(i<=bsize){ bit[i]+=x; i+=i&(-i); } } int N,Q; int A[111111]; int l[111111],r[111111]; int P[111111]; int whole=0; long long result=0; void add(int i,bool isLeft){ if(isLeft){ result+=BIT_sum(A[i]-1); } else{ result+=whole; result-=BIT_sum(A[i]); } BIT_add(A[i],1); whole++; } void rem(int i,bool isLeft){ BIT_add(A[i],-1); whole--; if(isLeft){ result-=BIT_sum(A[i]-1); } else{ result-=whole; result+=BIT_sum(A[i]); } } int cur_l,cur_r; long long mo_work(int l,int r){ while(l < cur_l){cur_l--;add(cur_l,true);} while(cur_r < r){cur_r++;add(cur_r,false);} while(cur_l < l){rem(cur_l,true);cur_l++;} while(r < cur_r){rem(cur_r,false);cur_r--;} return result; } long long output[111111]; int main(){ // 入力処理 cin >> N >> Q; bsize=N; for(int i=1;i<=N;i++){ cin >> A[i]; } for(int i=1;i<=Q;i++){ cin >> l[i] >> r[i]; } // 座標圧縮 map<int,int> mp; for(int i=1;i<=N;i++){ mp[A[i]]=1; } int comp=1; for(auto &nx : mp){ nx.second=comp; comp++; } for(int i=1;i<=N;i++){ A[i]=mp[A[i]]; } // 順列 P をうまく定める for(int i=1;i<=Q;i++){ P[i] = ( 出力された P_i ); } // 計時開始 start_clock(); for(int i=1;i<=Q;i++){ if(i==1){ cur_l=l[P[i]]; cur_r=l[P[i]]; add(l[P[i]],true); } output[P[i]]=mo_work(l[P[i]],r[P[i]]); if(i%100==0){ // TLEノックアウト if(current_clock()>1500000){break;} } } // 計時終了 long long exe_time=current_clock(); // 出力 for(int i=1;i<=Q;i++){ cout << output[i] << "\n"; } return 0; }
コード の簡単な説明:
- 入力を受け取る。
- を座標圧縮する。
- 出力された順列 を受け取る。
- Mo's Algorithm と同じ要領で区間の転倒数を求めていく。
- 番目には 番目の問い合わせの区間に対して答えを求める。
- 区間の 左端 / 右端 の 点 追加 / 削除 を行う際、 Binary Indexed Tree を利用して転倒数の変動を求める。
- その際の計算量は である。
- Mo's Algorithm を ( のソートという形で ) うまく実装すれば という計算量にできる。
あなたの目的は、この順列 をうまく定めて物理好きさんのコードをなるべく高速に実行させることです。
但し、 を定めるのに使える時間は ms しかありません。
なぜなら、 Mo's Algorithm の本質部分に実行時間を ms 残し、 s の実行時間制限に間に合わせたいからです。
あなたはより優秀な順列 を高速に見つけることができますか?
得点
この問題では、出力された順列 を利用して物理好きさんのコード を実際に実行した結果により得点が定められます。
この問題には 100 個のテストケースが含まれます。
コード が各テストケースに対して μs の実行時間を要したとします。
すると、各テストケースに対する得点は以下のように定められます。
- (実行時間が ms 以下) である場合、得点は 点である。
- そうでない場合、得点は 点である。
なので、この得点は ( ms 以内に AC したテストケース数) ( AC したテストケースについての残り実行時間の合計 (μs) ) と表現できます。
実行時間 はジャッジコード内の変数
exe_time
の値により決定されます。これは、順列 を定める実行時間制限 ms のうちどれだけを消費したかには非依存です。
なお、 点以上が獲得できる (すなわち、全てのテストケースについてコード が TLE しない) プログラムが存在することを保証します。
入力
入力は Static Range Inversions Query の入力です。物理好きさんのコードにも同じものが与えられて実行されます。
出力
順列 を以下の形式で出力せよ。
入力生成方法
入力のうち ケースは以下の方法により生成されます。
区間生成法 を満たす整数組 は 通り考えられますが、各整数組に対応するテストケースが つずつ生成されます。
は の範囲で独立一様ランダムに生成されます。
それに加えて、入力のうち ケースは以下の方法により生成されます。
で固定です。
の種類数 であるケースが区間生成法 について各 個ずつ、計 ケースが生成されます。
から始め、後ろに 以上 以下の一様乱数を 個付けたのち をシャッフル、その後 同一の値が同一になる / 異なる値が異なる ように各値を 以上 以下の一様ランダムな整数に付け替えます。
それに加えて、入力のうち ケースは以下で示される サンプル1 に一致します。
それに加えて、入力のうち ケースは こちらで公開されています。 として生成したケースです。
は の範囲で独立一様ランダムに生成されています。
残りの ケースにについて、入力生成方法は秘匿されます。どのような悪意を持ったケースも与えられる可能性があります。
但し、ジェネレータ欄にジェネレータを貼っています。コンテスト終了後などに参照してください。
各区間生成法の詳細は以下の通りです。
方法1:
- 区間は必ず以下の方法で生成される。
- で生成する。
- もし なら を交換する。
- 生成される区間は である。
- 区間は必ず以下の方法で生成される。
- で生成する。
- で生成し、 とする。
- 生成される区間は である。
- 区間は 方法1 と 方法2 のどちらかが等確率で区間ごとに独立に選択され、その方法で生成される。
サンプル
サンプル1
入力
5 4 1 0 3 2 2 1 4 2 3 2 5 1 5
出力
4 2 1 3
もし、この出力に対してコード が μs ( ms ) で動作した場合、このケースに対する得点は 点となります。
もし、この出力に対してコード が μs ( ms ) で動作した場合やコードが TLE ノックアウトした場合は、このケースに対する得点は 点となります。
ちなみに、本来の問題の正答は以下の通りです。
2 0 2 3
提出するには、Twitter 、GitHub、 Googleもしくは右上の雲マークをクリックしてアカウントを作成してください。