zoukankan      html  css  js  c++  java
  • 牛客网暑期ACM多校训练营(第二场)G transform 思维,二分

    G transform

    题意:
    X轴上有 n 个箱子,在 X[i] 坐标的箱子里有 a[i] 个物品。把第 i 个箱子里的一个物品移动到第 j 个箱子,花费为 abs(X[i]-X[j]) 。可以在花费<=T/2 的情况下移动物品,最后要选择一个箱子取出它所有的物品,问最多可以取出多少个物品?
    题解:
    参考了题解https://www.nowcoder.com/discuss/88268?type=101&order=0&pos=1&page=1
    1、首先明确所移动的物品一定是一个连续区间。可以这样想:假定我们最后选择取第 i 个箱子,那我们肯定是依次取它左右离它最近的箱子,这样是最优的,也就是一个区间。
    2、选择了一个区间,那肯定是把物品移动到中间个数点最优,比如:10,1,1,1,1 中间个数点在第一个位置,肯定是把其它位置点移动到第一个位置最优。
    理由:假如所有箱子只有一个物品,比如 1,1,1,1,1 那肯定移动到最中间最优。 我们可扩展想一下,一个箱子里有 a[i] 个物品,把它变为这个位置有 a[i] 个箱子,所以也是移动到最中间的箱子里。
    3、知道前两点就可以二分求解了。二分答案,最多可以取 ans 个物品。每次二分枚举区间左端点 l ,然后移动右端点 r ,直到区间物品个数和 >=ans,然后看这个区间的最小花费是否<=T/2 。
    求一个区间的最小花费,我们先枚举找出中间位置 mid,然后计算 区间 [l,mid] 所有物品移动到右端点的花费 + 区间 [mid,r] 所有物品移动到左端点的花费, 这即是最小花费。所以预处理一个前缀和就好了。

    sum[i]表示前 i 个 a[i]*x[i] 的和 ,sumA[i]表示前 i 个 a[i] 的和, cal1(l,r) 表示区间 [l,r] 所有物品移动到左端点的花费,cal2(l,r)表示区间 [l,r] 所有物品移动到右端点的花费。

    
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 500005;
    
    int n, a[N], X[N];
    ll  T, sum[N], sumA[N];
    ll  cal1(int l, int r) {
        return sum[r] - sum[l-1] - (sumA[r]-sumA[l-1])*X[l];
    }
    ll  cal2(int l, int r) {
        return (sumA[r]-sumA[l-1])*X[r] - (sum[r]-sum[l-1]);
    }
    bool check(ll aim)
    {
        for(int l=1, r=1, mid=1; ; ++l)
        {
            while(r<=n && sumA[r]-sumA[l-1]<aim) ++r;
            while(mid<=r && (sumA[mid]-sumA[l-1])*2<sumA[r]-sumA[l-1]) ++mid;
            if(r > n) break;
            ll  tmp = sumA[r]-sumA[l-1]-aim;
            if(cal2(l,mid)+cal1(mid,r)-tmp*(X[r]-X[mid]) <= T) return true;
        }
        for(int l=n, r=n, mid=n; ; --r)
        {
            while(l>0 && sumA[r]-sumA[l-1]<aim) --l;
            while(l<=mid && (sumA[r]-sumA[mid-1])*2<sumA[r]-sumA[l-1]) --mid;
            if(l < 1) break;
            ll  tmp = sumA[r]-sumA[l-1]-aim;
            if(cal2(l,mid)+cal1(mid,r)-tmp*(X[mid]-X[l]) <= T) return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d%lld", &n, &T);  T>>=1;
        rep(i,1,n) scanf("%d", &X[i]);
        rep(i,1,n) {
            scanf("%d", &a[i]);
            sumA[i] = sumA[i-1] + a[i];
            sum[i] = sum[i-1] + 1LL*a[i]*X[i];
        }
        ll  l=0, r=1e10, mid, ans;
        while(l <= r) {
            mid = l+(r-l)/2;
            if(check(mid)) ans=mid, l=mid+1;
            else  r=mid-1;
        }
        printf("%lld
    ", ans);
    
        return 0;
    }
    
  • 相关阅读:
    mysql分表技术(学习心得)
    Linux下搭建DNS服务器
    php简单单例模式
    JQuery的ajaxFileUpload图片上传初试
    Binary Tree Level Order Traversal II
    Remove Element
    Symmetric Tree
    Balanced Binary Tree
    Power of Two
    Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/sbfhy/p/9424628.html
Copyright © 2011-2022 走看看