ricehub bzoj-2600 Ioi-2011
题目大意:在数轴上有r块稻田,稻田坐标为整数。计划建造一个米仓,使得它可以收取尽量多的稻米。米仓的坐标仍需为整数。每一块权值为val的稻田距离米仓的距离为dis,那么收取这个稻田上的大米的代价是val*dis。我们只有b元运费,问最多能收取多少稻米。
注释:$1le r le 10^5$,$1le lle 10^9$,$0le ble 2cdot 10^{15}$。
想法:这就相当于在一个带权的数轴上选取一个点,使得所有点到该点的加权*距离最小。我们不妨将权值为val的点看成是val个不同的点。那么这就相当于在数轴上$sumlimits_{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 ",r); else printf("%d ",l); return 0; }
小结:对于加权的问题,我们可以将它拆掉,看成不加权的问题。