ricehub bzoj-2600 Ioi-2011

题目大意:在数轴上有r块稻田,稻田坐标为整数。计划建造一个米仓,使得它可以收取尽量多的稻米。米仓的坐标仍需为整数。每一块权值为val的稻田距离米仓的距离为dis,那么收取这个稻田上的大米的代价是val*dis。我们只有b元运费,问最多能收取多少稻米。

注释:$1\le r \le 10^5$,$1\le l\le 10^9$,$0\le b\le 2\cdot 10^{15}$。

想法:这就相当于在一个带权的数轴上选取一个点,使得所有点到该点的加权*距离最小。我们不妨将权值为val的点看成是val个不同的点。那么这就相当于在数轴上$\sum\limits_{i=1}^{r}val_i$个点,所有点到被选取点的距离(由于已经将点拆过了,所以不加权)最小。显然,是在所有点的中位数(每一个点开成val个)的位置距离和最小。这样的话我们二分,对于每一个二分过程中的中间点check一下即可。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,m,a[100005];
ll tot,sum[100005];
bool check(int x)
{
    int j,mid,pos;
    for(int i=1;i+x-1<=n;i++)
    {
        j=i+x-1;
        mid=i+(x>>1)-1;
        pos=j-(x>>1)+1;
        if(sum[j]-sum[pos-1]-sum[mid]+sum[i-1]<=tot)
            return true;
    }
    return false;
}
int main()
{
    scanf("%d%d%lld",&n,&m,&tot);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    int l=0,r=n;
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(check(mid))
            l=mid;
        else r=mid-1;
    }
    if(check(r)) printf("%d\n",r);
    else printf("%d\n",l);
    return 0;
}

小结:对于加权的问题,我们可以将它拆掉,看成不加权的问题。