POJ一般
- auto使えない
- using使えない
- include bits使えない
苦労したこと
- 問題文の読み間違い。Aは負値もある
- cinではなくscanfを使わないとTLE(問題文に書いてある)
- scanfで読み込み先がintかlong longかで指定を変える必要(変えないと変な値が入る)
- Time Limitが厳しいので、二分探索するにも-inf~infでは間に合わないので、ありうる値のみを探索する
- ありうる値のsort済み配列を用意して左右から範囲を絞り込んでいく
- 平方分割のサイズを適切にしないと通らない
- バケットサイズB=900でAC, 800や1000だとTLE
- (平方分割は計算量的に厳しいことを体感した)
- long longだと通らない
- Aの配列の型としてlong longを使っていたが、これだとB=900でも通らない。intに書き換えるとAC
- 入力がボトルネックでそこのメモリサイズが2倍になる
その他
- includeを削っても意味がない
- bitsが使えないので全部展開したが不要なのも多いので削ったが改善には貢献しなかった
- 配列のコピーはTLEの成否を分ける程ではない
- coutをprintfにするのは効果なし(クエリが5000個なのでボトルネックではない)
- とはいえscanfを使い始めるとcinを同居させたくなくなる(同居は可能)↓
計算量
- 配列の長さ N=100000
- バケットサイズ B=900
- クエリ数 Q=5000
- .
- 1回のcountを呼ぶのにN/B x log2(B) = 1090
- N=100000を二分探索するのでlog2(N)=16.6
- 全体として、1090 x 16.6 x 5000 = 90470000 = 108 = ギリギリ
- 全体計算量を記号で書くと
- O(N/B x log2(B) x log2(N) x Q)
ライブラリ 領域木
- これを作りたかったのだがPOJの落とし穴やTLでかなり時間がかかった
struct RangeTree{
static const int B = 900;
static const int MAXN = 110010;
VI A;
vector<VI> bucket;
ll N;
RangeTree(VI _A){
A = _A;
N=SZ(A);
bucket.resize(MAXN/B);
rep(i,N){
bucket[i/B].push_back(A[i]);
}
rep(i,MAXN/B){
SORT(bucket[i]);
}
}
ll count_naive(ll l, ll r, ll x){
ll cnt=0;
FOR(i,l,r){
if(A[i]<=x)cnt++;
}
return cnt;
}
ll count(ll l, ll r, ll x){
ll cnt=0;
while(l<r && l%B!=0){
if(A[l]<=x)cnt++;
l++;
}
while(l<r && r%B!=0){
if(A[r-1]<=x)cnt++;
r--;
}
while(l<r){
ll b = l/B;
ll num = upper_bound(ALL(bucket[b]), x) - bucket[b].begin();
cnt += num;
l += B;
}
return cnt;
}
};
AC (自分用) 他人からは見えない
all code (AC)
#ifdef LOCAL
#define __clock__
#else
#pragma GCC optimize("Ofast")
#endif
#include <algorithm>
#include <bitset>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <numeric>
#include <ostream>
#include <stdexcept>
#include <streambuf>
#include <vector>
using namespace std;
typedef int ll;
typedef vector<ll> VI;
#define FOR(i,a,b) for(ll i=(a);i<(b);++i)
#define rep(i,b) FOR(i, 0, b)
#define ALL(v) (v).begin(), (v).end()
#define p(s) cout<<(s)<<'\n'
#define p2(s, t) cout << (s) << " " << (t) << '\n'
#define br() p("")
#define pn(s) cout << (#s) << " " << (s) << '\n'
#define SZ(x) ((int)(x).size())
#define SORT(A) sort(ALL(A))
#define RSORT(A) sort(ALL(A), greater<ll>())
const ll mod = 1e9 + 7;
const ll inf = 1e9+10;
struct RangeTree{
static const int B = 900;
static const int MAXN = 110010;
VI A;
vector<VI> bucket;
ll N;
RangeTree(VI _A){
A = _A;
N=SZ(A);
bucket.resize(MAXN/B);
rep(i,N){
bucket[i/B].push_back(A[i]);
}
rep(i,MAXN/B){
SORT(bucket[i]);
}
}
ll count_naive(ll l, ll r, ll x){
ll cnt=0;
FOR(i,l,r){
if(A[i]<=x)cnt++;
}
return cnt;
}
ll count(ll l, ll r, ll x){
ll cnt=0;
while(l<r && l%B!=0){
if(A[l]<=x)cnt++;
l++;
}
while(l<r && r%B!=0){
if(A[r-1]<=x)cnt++;
r--;
}
while(l<r){
ll b = l/B;
ll num = upper_bound(ALL(bucket[b]), x) - bucket[b].begin();
cnt += num;
l += B;
}
return cnt;
}
};
int main(){
ll N,Q;
cin>>N>>Q;
VI A(N);
rep(i, N){
scanf("%d",&A[i]);
}
VI so = A;
SORT(so);
RangeTree tree(A);
while(Q--){
ll l,r,k;
cin>>l>>r>>k;
l--;r--;
ll left = -1;
ll right=N-1;
while(left+1!=right){
ll center = (left+right)/2;
ll v = so[center];
ll num = tree.count(l,r+1,v);
if(num<k){
left=center;
}else{
right=center;
}
if(left+1==right)break;
}
ll ans = so[right];
p(ans);
}
return 0;
}