Minimum Sum
Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3084 Accepted Submission(s): 710
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 我感觉划分树在遍历的时候最痛苦。哎,总是在推区间,区间真的很容易推错。 有一个数列 x1..xn,要求一个数x使得 ∑(abs(xi-x))值最小,很明显,对数列进行排序后最中间的那个数就是x,可用划分树求得,那么如何求和呢,经过计算可知,既然 x 是最中间的那个数,那么最后的和 即为 x左边 xmid-x1+xmid-x2.. + x(mid+1) - xmid + x(mid+2)-xmid.. 整理得 xmid*(lefnum-rignum)+rigsum-lefsum lefnum为划分过程进入左子树的个数,lefsum为进入左子树的数之和 上面是正常的思路,我来说一个非常规的思路,int ans = 0 ;我们直接遍历的时候,如果中位数为x,如果x在左子树,那我们ans+=区间内进入右子树所有数之和。如果x在右子树,那我们ans-=区间内进入左子树的,一直到找到x结束。这样我们就巧妙的上面的俩个过程合在了一起 哎,由于错把循环变量j写成了i,,拿着别人的代码调试了一晚上,结果代码和别人的长的差不多了,,无力 另外需要注意的是,,这道题对内存要求的比较紧,,数组不能太大。 下面是代码:#include <cstdio> #include <cstring> #include <algorithm> #define MAX 100100 using namespace std ; long long sum[25][MAX]; int sorted[MAX] , tree[25][MAX] ,toLeft[25][MAX] ; void creat(int L , int R , int deep) { if(L == R) { sum[deep][L]=tree[deep][L] ; return ; } int mid = (L+R)>>1 , same = mid-L+1; for(int i = L ; i <= R ; ++i) { if(tree[deep][i]<sorted[mid]) --same ; sum[deep][i] = tree[deep][i] ; if(i>L) sum[deep][i] += sum[deep][i-1] ; } int ls = L ,rs = mid+1; for(int i = L ; i <= R ; ++i) { int flag = 0 ; long long num = 0; if(tree[deep][i]<sorted[mid] || (tree[deep][i]==sorted[mid] && same)) { tree[deep+1][ls++]=tree[deep][i] ; if(tree[deep][i] == sorted[mid]) --same ; flag = 1 ; } else { tree[deep+1][rs++] = tree[deep][i] ; } toLeft[deep][i] = toLeft[deep][i-1]+flag ; } creat(L,mid,deep+1) ; creat(mid+1,R,deep+1) ; } long long ans = 0; int query(int L , int R , int x , int y , int k , int deep) { if(x == y) { return tree[deep][x] ; } int mid = (L+R)>>1; int lxl = toLeft[deep][x-1] - toLeft[deep][L-1] ; int lyl = toLeft[deep][y] - toLeft[deep][L-1] ; int xyl = toLeft[deep][y] - toLeft[deep][x-1] ; int lxr = x-L-lxl ; int lyr = y-L+1-(toLeft[deep][y]-toLeft[deep][L-1]) ; if(k<=xyl) { if(lyr>0) { if(lxr>0) { ans += sum[deep+1][mid+lyr]-sum[deep+1][mid+lxr]; } else { ans += sum[deep+1][mid+lyr] ; } } return query(L,mid,L+lxl,L+lyl-1,k,deep+1); } else { if(lyl>0) { if(lxl>0) ans-=sum[deep+1][L+lyl-1]-sum[deep+1][L+lxl-1]; else ans -= sum[deep+1][L+lyl-1] ; } return query(mid+1,R,mid+lxr+1,mid+lyr,k-xyl,deep+1); } } int main() { int t ; scanf("%d",&t); for(int i = 1 ; i <= t ; ++i) { int n ; scanf("%d",&n); memset(toLeft,0,sizeof(toLeft)) ; for(int j = 1 ; j <= n ; ++j) { scanf("%d",&sorted[j]) ; tree[0][j] = sorted[j] ; } sort(sorted+1 , sorted+n+1) ; creat(1,n,0) ; int r ; printf("Case #%d:\n",i) ; scanf("%d",&r) ; for(int j = 0 ; j < r ; ++j) { int l,ri; scanf("%d%d",&l,&ri) ; ++l,++ri; int k = (ri-l)/2+1; ans = 0 ; int d = query(1,n,l,ri,k,0); if((ri-l+1)%2 == 0) { ans -= d ; } printf("%I64d\n",ans); } printf("\n") ; } return 0 ; }