【hdu3473】【划分树】Mininum Sum

来源:互联网 发布:网络彩票代理怎么做的 编辑:程序博客网 时间:2024/04/30 03:59

题目大意: 给出一个数字序列,每次询问[l,r],要求从[l,r]中找出一个x,最小化abs(x - xi),i  [l,r]

首先容易得到,题目要求的是找出一段区间中位数,输出x - xix为中位数

去掉绝对值后,我们可以得到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;}