题目大意: 给出一个数字序列,每次询问[l,r],要求从[l,r]中找出一个x,最小化∑abs(x - xi),i ∈ [l,r]
首先容易得到,题目要求的是找出一段区间中位数,输出∑x - xi,x为中位数
去掉绝对值后,我们可以得到ans = x(leftnum - rightnum) + (rightsum - leftsum)
leftnum和leftsum分别表示中位数左边的数和中位数左边的和(都包含中位数),right同理。
一段区间内的中位数可以用划分树解决,同时维护一个sum域,在查询的时候把上述的值维护出来就行了。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn = 100000 + 10;
const int maxh = 20;
long long f[maxh][maxn],sum[maxh][maxn];
long long s[maxh][maxn],rank[maxn],total[maxn];
int num[maxn][2];
int n,m,h,ss = 0;
long long num_lessmid,sum_lessmid;
void init()
{
freopen("MinimumSum.in","r",stdin);
freopen("MinimumSum.out","w",stdout);
}
int cmp(const void *a,const void *b)
{
return ((int *)a)[0] - ((int *)b)[0];
}
void build(int h,int l,int r)
{
if(l == r)
{
sum[h][l] = num[rank[f[h][l]]][0];
return;
}
int m = (l + r + 2) >> 1;ss = 0;
for(int i = l;i <= r;i++)
{
if(rank[f[h][i]] < m)
{
ss++;
s[h][i] = ss;
sum[h][i] = sum[h][i-1] + num[rank[f[h][i]]][0];
f[h+1][l+ss-1] = f[h][i];
}
else
{
s[h][i] = ss;
sum[h][i] = sum[h][i-1];
f[h+1][m+i-l-ss] = f[h][i];
}
}
build(h+1,l,m-1);
build(h+1,m,r);
}
long long query(int h,int l,int r,int x,int y,int k)
{
int m,st,ed;
num_lessmid = 0;
sum_lessmid = 0;
while(true)
{
if(l == r)return num[rank[f[h][l]]][0];
m = (l + r + 2) >> 1;
st = s[h][x];ed = s[h][y];
int com = ed - st;
if(rank[f[h][x]] < m)com++,st--;
if(k > com)
{
num_lessmid += com;
sum_lessmid += sum[h][y] - sum[h][x];
if(rank[f[h][x]] < m)sum_lessmid += num[rank[f[h][x]]][0];
x = m + x - l - st;
y = y - l - ed + m;
k = k - com;
l = m;
}
else
{
r = m - 1;
x = l + st;
y = l + ed - 1;
}
h++;
}
}
long long ask(int l,int r)
{
long long median = query(0,1,n,l,r,(r - l) / 2 + 1);
long long ans = median * (long long)(num_lessmid - (r - l + 1 - num_lessmid));
ans += (total[r] - total[l-1]) - sum_lessmid - sum_lessmid;
return ans;
}
void readdata()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&num[i][0]);
total[i] = total[i-1] + num[i][0];
num[i][1] = i;
}
qsort(num + 1,n,sizeof(int)*2,cmp);
for(int i = 1;i <= n;i++)rank[num[i][1]] = i;
h = 0;
for(int i = 1;i <= n;i++)f[0][i] = i;
memset(s,0,sizeof(s));
build(0,1,n);
scanf("%d",&m);
for(int i = 1;i <= m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%lld\n",ask(l + 1,r + 1));
}
}
int main()
{
init();
readdata();
return 0;
}