標籤:劃分樹
連結:http://acm.hdu.edu.cn/showproblem.php?pid=3473
Minimum SumTime Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3427 Accepted Submission(s): 787
Problem DescriptionYou 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!
InputThe 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.
OutputFor 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
253 6 2 2 421 40 227 720 11 1
Sample Output
Case #1:64Case #2:00
題意:
問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 __int64int 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];elses=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;}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
hdu 3473 Minimum Sum 劃分樹的應用