http://acm.hdu.edu.cn/showproblem.php?pid=3473、
题意:
给定一个长度为n的序列,求区间[l,r]内的一个点值为x,使得最小。
思路:
很多解题报告都说找出区间[l,r]的中位数即可,可是我百度了一下中位数,他的定义是这样的:
当变量值的项数N为奇数时,处于中间位置的变量值即为中位数;当N为偶数时,中位数则为处于中间位置的2个变量值的平均数,也即当N为偶数时,中位数就不一定属于该序列了。所以这里不是求得中位数。
而是当N为奇数时得到中位数k,偶数时N/2(k)位置的那个数,也即这里保证x来自该序列。然后利用划分树求出区间[l,r]中小于k的个数以及他们的和,然后利用公式计算即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 100007 using namespace std; //freopen("din.txt","r",stdin); struct node{ int l,r; int mid(){ return (l + r)>>1; } }tt[N<<2]; int toLeft[20][N]; int sorted[N],val[20][N]; ll Lsum[20][N],sum[N]; ll lnum,rnum,lsum,rsum; int n,m; void build(int l,int r,int rt,int d){ int i; tt[rt].l = l; tt[rt].r = r; if (l == r) return ; int m = tt[rt].mid(); int lsame = m - l + 1; for (i = l; i <= r; ++i){ if (val[d][i] < sorted[m]){ lsame--; } } int lpos = l, rpos = m + 1; int same = 0; for (i = l; i <= r; ++i){ if (i == l){ toLeft[d][i] = 0; Lsum[d][i] = 0; } else{ toLeft[d][i] = toLeft[d][i - 1]; Lsum[d][i] = Lsum[d][i - 1]; } if (val[d][i] < sorted[m]){ toLeft[d][i]++; Lsum[d][i] += val[d][i]; val[d + 1][lpos++] = val[d][i]; } else if (val[d][i] > sorted[m]){ val[d + 1][rpos++] = val[d][i]; } else{ if (same < lsame){ same++; toLeft[d][i]++; Lsum[d][i] += val[d][i]; val[d + 1][lpos++] = val[d][i]; } else{ val[d + 1][rpos++] = val[d][i]; } } } build(lc,d + 1); build(rc,d + 1); } int query(int L,int R,int k,int d,int rt){ ll tmp = 0;//记录小于第k大数的所有数的和 if (L == R){ //lnum++; //lsum += val[d][L];//这里可有可无因为求的时候它始终是0 return val[d][L]; } int s,ss; if (L == tt[rt].l){ ss = 0; s = toLeft[d][R]; tmp = Lsum[d][R]; } else{ ss = toLeft[d][L - 1]; s = toLeft[d][R] - toLeft[d][L - 1]; tmp = Lsum[d][R] - Lsum[d][L - 1]; } if (s >= k){ int newl = tt[rt].l + ss; int newr = newl + s - 1; return query(newl,newr,k,d + 1,rt<<1); } else{ lnum += s;//个数 lsum += tmp;//和 int m = tt[rt].mid(); int bb = L - tt[rt].l - ss; int b = R - L + 1 - s; int newl = m + bb + 1; int newr = newl + b - 1; return query(newl,newr,k - s,d + 1,rt<<1|1); } } int main(){ // freopen("din.txt","r",stdin); int t,cas = 1; int i; int x,y; scanf("%d",&t); while (t--){ printf("Case #%d:\n",cas++); scanf("%d",&n); sum[0] = 0; for (i = 1; i <= n; ++i){ scanf("%d",&val[0][i]); sum[i] = sum[i - 1] + val[0][i]; sorted[i] = val[0][i]; } //for (i = 1; i <= n; ++i) printf("") sort(sorted + 1,sorted + 1 + n); build(1,n,1,0);//建树 scanf("%d",&m); while (m--){ scanf("%d%d",&x,&y); x++; y++; int k = (y - x + 1); if (k&1) k = k/2 + 1; else k = k/2;//求出k lnum = 0; lsum = 0; ll ave = query(x,y,k,0,1); rsum = sum[y] - sum[x - 1] - lsum;//所有大于ave的和 rnum = y - x + 1 - lnum;//大于ave的个数 ll ans = rsum - rnum*ave + lnum*ave - lsum; printf("%I64d\n",ans); } printf("\n"); } return 0; }