题意:
一段固定不变的数字 m次询问 每次询问选择一个x值 使得区间[l,r]中每个元素与x的差的绝对值的和最小
思路:
x值明显选择[l,r]中数字的中位数 那么题目就变成了[l,r]中第(r-l+1+1)/2小的数是几 由于数字是静态的 所以划分树可解
那么ans = num(<=x) * x - sum(<=x) + sum(>x) - num(>x) * x
由于sum之间可由前缀和相互求出 num也可以通过区间大小相互推出 因此只需要求 x 、 sum(<=x) 、 num(<=x)
最基本的划分树可以解决x和num的求解
想求sum可以维护一个sum[i][j]数组 表示第i层到j位置为止进入左子树的数字的和
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 101000 #define M 30 typedef __int64 LL; int tree[M][N],toleft[M][N],sorted[N]; LL sum[M][N],before[N]; int t,T,n,m; void build(int l,int r,int dep) { if(l==r) return ; int i,mid=(l+r)>>1; int y=sorted[mid],same=mid-l+1,lpos=l,rpos=mid+1; for(i=l;i<=r;i++) { if(tree[dep][i]<y) same--; } for(i=l;i<=r;i++) { sum[dep][i]=sum[dep][i-1]; if(tree[dep][i]<y) { tree[dep+1][lpos++]=tree[dep][i]; sum[dep][i]+=tree[dep][i]; } else if(tree[dep][i]==y&&same>0) { tree[dep+1][lpos++]=tree[dep][i]; same--; sum[dep][i]+=tree[dep][i]; } else tree[dep+1][rpos++]=tree[dep][i]; toleft[dep][i]=toleft[dep][l-1]+lpos-l; } build(l,mid,dep+1); build(mid+1,r,dep+1); } int ansnum; LL anssum; int query(int L,int R,int l,int r,int dep,int k) { if(l==r) return tree[dep][l]; int mid=(L+R)>>1,amt=toleft[dep][r]-toleft[dep][l-1]; if(amt>=k) { int fl=L+toleft[dep][l-1]-toleft[dep][L-1]; int fr=fl+amt-1; return query(L,mid,fl,fr,dep+1,k); } else { ansnum+=amt; anssum+=sum[dep][r]-sum[dep][l-1]; int fr=r+toleft[dep][R]-toleft[dep][r]; int fl=fr-(r-l-amt); return query(mid+1,R,fl,fr,dep+1,k-amt); } } int main() { int i,l,r; LL middle; scanf("%d",&T); for(t=1;t<=T;t++) { printf("Case #%d:\n",t); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&sorted[i]); before[i]=before[i-1]+sorted[i]; tree[0][i]=sorted[i]; } sort(sorted+1,sorted+1+n); build(1,n,0); scanf("%d",&m); while(m--) { scanf("%d%d",&l,&r); l++; r++; ansnum=0; anssum=0; middle=(LL)(query(1,n,l,r,0,(r-l+2)/2)); printf("%I64d\n",(before[r]-before[l-1]-anssum)-anssum-middle*(r-l+1-ansnum-ansnum)); } puts(""); } return 0; }