2018牛客网暑期ACM多校训练营(第二场)G Transform(二分)
题意
在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有a[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。
分析
既然要使得花费在不超过T的情况尽可能多的移动货物,那么我们肯定是将一个区间内的所有货物移到坐标中位的集装箱上。那么我们就可以对答案进行二分,然后枚举所要移动的区间的左端点,再找到中位点和右端点,然后判断这个区间移动的花费是否小于T。
可以预处理一下前缀和以及前缀花费、距离等。依次来计算区间的花费。
二分判断的过程中,由于最终的答案不一定是将整个区间内所有的货物都移动到一个集装箱,所以我们还要判断比需求量多的那部分是从左端点移过来的还剩从右端点移过来的,然后再根据花费的大小情况进行二分就可以得到最终答案了。
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<string> #include<cstring> using namespace std; #define ll long long const int N = 5e5 + 10; ll dis[N]; ///dis[i]表示第i个集装箱距离x=0的距离 ll num[N]; ///num[i]表示第i个集装箱有的产品个数 ll sum[N]; ///sum[i]表示第1~i个集装箱有的产品数总和 ll cost[N]; ///cost[i]表示第1~i个集装箱的所有产品移到x=0处所"获得"的费用 ll tot,q; int n, L, R, u; ///u为产品汇总的最优点 ll cost_l(int i) {///费用=将L+1~i中所有产品移动到i处所需费用+(将i~R中所有的产品移动到i处所需的费用 - 区间L+1~R比x多的产品数从R处移动到i处的费用) return ((sum[i] - sum[L])*dis[i] - (cost[i] - cost[L])) + ((cost[R] - cost[i]) - (sum[R] - sum[i])*dis[i] - (sum[R] - sum[L] - q)*(dis[R] - dis[i])); } ll cost_r(int i) {///同上,只是规则是优先从右边界开始取产品 return -((sum[R] - sum[i])*dis[i] - (cost[R] - cost[i])) - ((cost[i-1] - cost[L]) - (sum[i-1] - sum[L])*dis[i] + (sum[R] - sum[L] - q)*(dis[i] - dis[L+1])); } bool check(ll &x) { ///判断需求x是否能在所给的费用t内达到 q = x; L = 0, R = 1, u = 1; while (1) { ///从左边界开始向右移动区间,优先取区间左边的产品 while (R < n&&sum[R] - sum[L] < x) R++; if (sum[R] - sum[L] < x)break; ///若是当前的L~n无法满足x,那么L++也不可能满足 while (u < L)u++; while (u < R&&cost_l(u)>cost_l(u + 1))u++; if (cost_l(u) <= tot)return true; L++; } L = n - 1, R = n, u = n; while (1) { ///从右边界开始向左移动区间,优先取区间右边的产品 while (L > 0 && sum[R] - sum[L] < x) L--; if (sum[R] - sum[L] < x)break; ///若是当前的L~R无法满足x,那么R--也不可能满足 while (u > R)u--; while (u > L && cost_r(u) > cost_r(u - 1))u--; if (cost_r(u) <= tot)return true; R--; } return false; } int main() { scanf("%d%lld", &n, &tot); tot /= 2; for (int i = 1; i <= n; i++) scanf("%lld", &dis[i]); sum[0]=cost[0]=0; for (int i = 1; i <= n; i++) { scanf("%lld", &num[i]); sum[i] = sum[i - 1] + num[i]; cost[i] = cost[i - 1] + num[i] * dis[i]; } ll l = 0,r = sum[n] + 1; while (l + 1 < r) { ll mid = (l + r) >> 1; if (check(mid)) l = mid; else r = mid; } printf("%lld\n", l); return 0; }