2 5 3 6 2 2 4 2 1 4 0 2 2 7 7 2 0 1 1 1
Case #1: 6 4 Case #2: 0 0
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3473
题意:给你一个数列,每次询问一个区间,要求找到一个数,使得区间所有数与它的差的绝对值之和最小。。。
分析:这里涉及到一个常识,那个数就是区间的中位数,那么原问题就转成,询问一个区间的中位数与所有数的差,可以用划分树解决。。。
具体的实现方案,只要添加一个当前小于中间值的数的和即可,询问时再做统计,最后根据简单的加减就能算出答案了
代码:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int mm=111111; int a[mm],s[22][mm],g[22][mm]; long long w[22][mm],sum[mm],tmp,ans; int i,j,k,n,m,t,cs=0; void build(int l,int r,int d) { if(l==r)return; int i,t1,t2,same=0,m=(l+r)>>1; for(i=l; i<=r; ++i) w[d][i]=g[d][i]=0,same+=(s[d][i]<a[m]); same=m-l+1-same; for(t1=i=l,t2=m+1; i<=r; ++i) if(s[d][i]==a[m]&&same)--same,s[d+1][t1++]=s[d][i],g[d][i]=1,w[d][i]=s[d][i]; else if(s[d][i]<a[m]) s[d+1][t1++]=s[d][i],g[d][i]=1,w[d][i]=s[d][i]; else s[d+1][t2++]=s[d][i]; for(i=l; i<r; ++i) g[d][i+1]+=g[d][i],w[d][i+1]+=w[d][i]; build(l,m,d+1); build(m+1,r,d+1); } int query(int L,int R,int k,int l,int r,int d) { int m,w1,w2; long long tmp; while(l<r) { m=(l+r)>>1,w1=0,w2=g[d][R],tmp=0; if(L>l)w2-=(w1=g[d][L-1]),tmp=w[d][L-1]; if(w2>=k)r=m; else ans-=(w[d][R]-tmp), k-=w2,w1=L-l-w1,w2=R-L+1-w2,l=m+1; L=l+w1,R=l+w1+w2-1,++d; } return s[d][l]; } int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=1; i<=n; ++i) { scanf("%d",&a[i]); s[0][i]=a[i]; sum[i]=sum[i-1]+a[i]; } sort(a+1,a+n+1); build(1,n,0); scanf("%d",&m); printf("Case #%d:\n",++cs); while(m--) { scanf("%d%d",&i,&j); ++i,++j; k=(j-i+2)/2; ans=0; tmp=query(i,j,k,1,n,0); ans=sum[j]-sum[i-1]+2*ans-tmp*(j-i+2-k)+tmp*(k-1); printf("%I64d\n",ans); } puts(""); } return 0; }