链接:http://acm.hdu.edu.cn/showproblem.php?pid=3473
Minimum Sum
Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3427 Accepted Submission(s): 787
Problem Description
You are given N positive integers, denoted as x0, x1 ... xN-1. Then give you some intervals [l, r]. For each interval, you need to find a number x to make as small as possible!
Input
The first line is an integer T (T <= 10), indicating the number of test cases. For each test case, an integer N (1 <= N <= 100,000) comes first. Then comes N positive integers x (1 <= x <= 1,000, 000,000) in the next line. Finally, comes an integer Q (1 <=
Q <= 100,000), indicting there are Q queries. Each query consists of two integers l, r (0 <= l <= r < N), meaning the interval you should deal with.
Output
For the k-th test case, first output “Case #k:” in a separate line. Then output Q lines, each line is the minimum value of . Output a blank line after every test case.
Sample Input
2 5 3 6 2 2 4 2 1 4 0 2 2 7 7 2 0 1 1 1
Sample Output
Case #1: 6 4 Case #2: 0 0
题意:
问l,r区间 取任意整数x , 问 ∑|X-xi| 的最小值
做法:很明显 这个X是中位数。 中位数想到划分树,然后在划分树 建树的时候,把进左子树的数计算一个前缀和,然后在查询的时候,如果到右子树,就把区间内进入左子树的数算一个和。最后统计出来的sumlft 就是所有比区间内中位数小的数了。
//O(log(n)) 找第k大的值 #include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define N 100009 #define M 19 //M>log2,N; #define MID ((l+r)>>1) #define ll __int64 int s[N];//s存的是 sort后的原始数据; int t[M][N];//t存的是树; int numb[M][N];//和t数组同步,当前层,当前区间,N下标前有多少个数划归到左子树. int n,m; ll quzuo[M][N]; ll sum[N]; void Build(int c,int l,int r) //c是层数 { int lm=MID-l+1,lp=l,rp=MID+1; //lp左边的起点 rp 右边的起点 for(int i=l;i<=MID;i++) lm-=s[i]<s[MID]; //lm 代表 现在的左块(下标l-MID)中 有多少等于s[MID]的 包括其本身 int zuo,you; for(int i=l;i<=r;i++) { zuo=you=0; if( i==l ) { numb[c][i]=0; quzuo[c][i]=0; } else { numb[c][i]=numb[c][i-1]; quzuo[c][i]=quzuo[c][i-1]; } if(t[c][i]==s[MID]) { if( lm )//左边有多少等于中位数的的 都归到左块去 { lm--; zuo=1; } else//如果 等于左块的中位数用完了, 就放到右块去 you=1; } else if( t[c][i]<s[MID] )//小的去左块 zuo=1; else you=1; if(zuo) { numb[c][i]++; t[c+1][lp++]=t[c][i]; quzuo[c][i]+=t[c][i]; } else { t[c+1][rp++]=t[c][i]; } } if( l<r ) Build(c+1,l,MID),Build(c+1,MID+1,r); } __int64 sumlft,sumrit; int numlft,numrit; int Query(int c,int l,int r,int ql,int qr,int k)//ql和qr是查询的区间左右边界, l和r 当前区间. { if( l==r ) return t[c][l]; int s,ss; if( l==ql ) s=0,ss=numb[c][qr]; else s=numb[c][ql-1],ss=numb[c][qr]-numb[c][ql-1]; if( k<=ss ) { return Query(c+1,l,MID,l+s,l+s+ss-1,k); } else { if(ql==l) { sumlft+=quzuo[c][qr]-0; } else { sumlft+=quzuo[c][qr]-quzuo[c][ql-1]; } return Query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss);// ss到左子树 所以ss在右子树里排名下降ss } } int main() { int tt; int cas=1; scanf("%d",&tt); while(tt--) { scanf("%d",&n); sum[0]=0; for(int i=1;i<=n;i++) { s[i]=i; scanf("%d",&s[i]); t[0][i]=s[i]; sum[i]=sum[i-1]+(ll)s[i]; } sort(s+1,s+1+n); Build(0,1,n); scanf("%d",&m); printf("Case #%d:\n",cas++); while( m-- ) { int l,r; sumlft=sumrit=numlft=numrit=0;//在进入右子树的时候,把区间内 到左子树的值加到sumlft中 scanf("%d%d",&l,&r);// l-r 中 第k大,l从1开始 c=0开始, l++; r++; int ans; ans=Query(0,1,n,l,r,(r-l)/2+1); numlft=(r-l)/2; numrit=r-l-numlft; sumrit=sum[r]-sum[l-1]-sumlft-(ll)ans; // printf("zuohe %d youhe %d zuoshu %d youshu %d ans%d \n",sumlft,sumrit,numlft,numrit,ans); printf("%I64d\n",((ll)numlft*(ll)ans-sumlft)+(sumrit-(ll)numrit*(ll)ans)); } puts(""); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。