HDU 3473 Minimum Sum

题意:

一段固定不变的数字  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;
}


你可能感兴趣的:(数据结构,HDU)