- https://ac.nowcoder.com/acm/contest/140/G
- 题意:在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有a[i]件货物。
- 现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) )。
- 求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。
- 思路:既然要使得花费在不超过T的情况尽可能多的移动货物,那么我们肯定是将一个区间内的所有货物,
- 移到坐标中位的集装箱上。那么我们就可以对答案进行二分,然后枚举所要移动的区间的左端点,
- 再找到中位点和右端点,然后判断这个区间移动的花费是否小于T,之所以正反打两边尺取是因为,当数目是
- 偶数时,中位数位置可能有多个选择,而且我们枚举的区间内的数目也不一定恰好等于当前而二分到的答案
- 我们就默认从左端开始枚举区间时中位数位置选取靠左的,从右边枚举区间时,中位数选取位置靠右的。
- 这样在处理 (区间内的数目也不一定恰好等于当前而二分到的答案)这种情况时,在两类枚举状态下分别有
- 最优的选择,当从左边枚举时中位数选取靠左的,多出的数目自然是扔掉右边的 最优,
- 同理,当从右边枚举时中位数选取靠右的,多出的数目自然是扔掉左边的 最优,
-
#include<bits/stdc++.h> using namespace std; #define maxn 555555 #define ll long long int n,a[maxn],x[maxn]; ll pr[maxn],s[maxn],t,l,r,mid; ll rpr[maxn],rs[maxn]; ll precal(int l,int r) { return pr[r]-pr[l-1]-s[l-1]*(x[r]-x[l-1]); } ll lastcal(int l,int r) { return rpr[l]-rpr[r+1]-rs[r+1]*(x[r+1]-x[l]); } bool ok(ll cur) { ll sum=(cur+1)/2,mod,w; int md,head,tail; head=tail=md=1; while(1) { while(tail<=n&&s[tail]-s[head-1]<cur)tail++; while(md<=n&&s[md]-s[head-1]<sum)md++; if(tail>n||md>n)break; mod=s[tail]-s[head-1]-cur; w=precal(head,md)+lastcal(md,tail)-mod*(x[tail]-x[md]); if(w<=t)return true; head++; } head=tail=md=n; while(1) { while(head>=1&&rs[head]-rs[tail+1]<cur)head--; while(md>=1&&rs[md]-rs[tail+1]<sum)md--; if(head<1||md<1)break; mod=rs[head]-rs[tail+1]-cur; w=precal(head,md)+lastcal(md,tail)-mod*(x[md]-x[head]); if(w<=t)return true; tail--; } return false; } int main() { scanf("%d%lld",&n,&t); t/=2; for(int i=1; i<=n; i++) scanf("%d",&x[i]); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; pr[i]=pr[i-1]+s[i-1]*(x[i]-x[i-1]); } for(int i=n; i>=1; i--) { rs[i]=rs[i+1]+a[i]; rpr[i]=rpr[i+1]+rs[i+1]*(x[i+1]-x[i]); } r=1e16; while(l+1<r) { mid=(l+r)/2; if(ok(mid))l=mid; else r=mid; } if(ok(r))printf("%lld\n",r); else printf("%lld\n",l); return 0; }
transform-二分-尺取-贪心
最新推荐文章于 2020-05-27 09:57:24 发布