题目传送门//res tp hdu
目的
对长度为n的区间,给定q个子区间,求一x,使得区间内所有元素与x的绝对值之和最小。
多测。
n 1e5
q 1e5
ai [1,1e9] (i∈[1,n]);
数据结构
划分树
tip
该划分树维护的cnt并非元素所在区间内,该元素之前进入左子树的元素个数,而是整个区间内,该元素之前进入左子树的元素个数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int L = 100000+50;
typedef long long ll;
int n,q;
int tree[18][L];
int cnt[18][L];
ll sum[18][L],ans;
int a[L],sorted[L];
void build(int de,int l,int r){
if(l == r){
sum[de][l] = sum[de][l-1] + tree[de][l];return;
}
if(de == 0){
for(int i = 1;i<=r;++i)
tree[de][i] = a[i];
}
int mi=(l+r)>>1;
int scnt = mi - l + 1;
ll M = sorted[mi];
for(int i = l;i<=mi;++i) if(sorted[i] < M)
scnt--;
int pl = l,pr = mi + 1;
for(int i = l;i<=r;++i){
ll num =tree[de][i];
if(num == M){
if(scnt){
scnt--;
tree[de+1][pl++] = num;
cnt[de][i]++;
}
else
tree[de+1][pr++] = num;
}
else if(num < M){
tree[de+1][pl++] = num;
cnt[de][i]++;
}
else
tree[de+1][pr++] = num;
sum[de][i] = sum[de][i-1] + tree[de][i];
cnt[de][i] += cnt[de][i-1];
}
build(de+1,l,mi);
build(de+1,mi+1,r);
}
ll query(int de,int L,int R,int l,int r,int k){
if(L == R) return tree[de][L];
int mi = (L + R)>>1;
int lo1,lo2,hi1,hi2;
int CNT = cnt[de][r] - cnt[de][l-1];
lo1 = L + cnt[de][l-1] - cnt[de][L-1];
hi1 = L + cnt[de][r] - cnt[de][L-1] - 1;
lo2 = mi + 1 + l - L - cnt[de][l-1] + cnt[de][L-1];
hi2 = mi + 1 + r - L - cnt[de][r] + cnt[de][L-1];
if(k <= CNT){
if(hi2 >= lo2)
ans += sum[de+1][hi2] - sum[de+1][lo2-1];
return query(de+1,L,mi,lo1,hi1,k);
}
else{
if(hi1>=lo1)
ans -= sum[de+1][hi1] - sum[de+1][lo1-1];
return query(de+1,mi+1,R,lo2,hi2, k - CNT);
}
}
int main(){
int T,l,r;
int icase = 1;
scanf(" %d",&T);
while(T--){
scanf(" %d",&n);
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
for(int i = 1;i<=n;++i){
scanf(" %d",a + i);
sorted[i] = a[i];
}
sort(sorted+1,sorted+1+n);
build(0,1,n);
scanf(" %d",&q);
printf("Case #%d:\n",icase++);
while(q--){
ans = 0;
scanf(" %d %d",&l,&r);
l++;r++;
ll t = query(0,1,n,l,r,(r - l)/2 + 1);
if((r-l)%2 == 1)
ans -= t;
printf("%lld\n", ans);
}
cout<<endl;
}
}