SRM 311 Div1 Medium / Div2 Hard FloatingMedian
データ構造のタグがないことに気づいた。
問題
長さNの数列aが与えられる。
aの連続するK個の部分数列全てについて、それぞれの中央値を合計するといくつになるか、求めよ。
ただし長さKの数列の中央値とは、(K+1)/2番目に小さい項の値を言う。
制約条件
N≦25万
K≦5000
0≦a[i]≦65535
方針
平方分割やセグメント木でk-th number的なことをやろうとするとTLE.
a[i]の値が65536通りしかないので、0以上j以下の項の数はBinary Indexed Tree使ってO(log k)で求めることができる。
よって、中央値は二分探索によりO(log 65536*log k)で求めることができる。
次の項を入れて、左端の項を出しながら、中央値を求めることをN-K+1回繰り返せばよい。
ソースコード
const int MX=300000,MX2=70000; int a[MX],N,bit[MX2+1]; int sum(int i){ int s=0; while(i>0)s+=bit[i], i-=i&-i; return s; } void add(int i,int x){ while(i<=MX2)bit[i]+=x, i+=i&-i; } class FloatingMedian { public: long long sumOfMedians(int seed, int mul, int ad, int N, int K) { ::N=N; a[0]=seed; rep(i,N-1)a[i+1]=((ll)a[i]*mul+ad)%65536; rep(i,MX2+1)bit[i]=0; ll ans=0; rep(i,K-1)add(a[i]+1,1); rep(i,N-K+1){ add(a[i+K-1]+1,1); int lo=-1,hi=65536,mid; while(lo+1<hi){ mid=(lo+hi)/2; if(sum(mid+1)<(K+1)/2)lo=mid; else hi=mid; } ans+=hi; add(a[i]+1,-1); } return ans; } };