【UOJ】#49.铀仓库

【算法】二分+贪心

【题意】转换模型后大概是:给定n,T,n个数字和n个坐标,自行选择一个起点,从起点往最近的数字跑一次往返拿回1,T为最长距离限制,求至多拿多少数字。

【题解】

比赛题解:http://vfleaking.blog.uoj.ac/blog/43

对于每个点从左到右,其能取的左右区间不一定都是单调的,所以很难O(n)。

考虑二分答案,将求值问题转变为判定问题。

二分答案,问题变为能否在T内取x个箱子。

然后发现这样对于每个点从左到右,左右区间是单调递增的,只需要每次比较右端未取和左端已取,贪心地舍去较差的。

简单地证明一下为什么区间不可能向左,基准点i移动到i+1,如果此时左边有未取点更优,则在i点处已取,不可能发生区间右移,区间右移仅当右边更优,而基准点右移同时也使右边更优,故区间单调。

#include<cstdio>
#include<algorithm>
#include<cctype>
#define ll long long
using namespace std;
const int maxn=500010;
ll cnt[maxn],sum[maxn],x[maxn],a[maxn],n,T;
ll read(){
    char c;ll s=0;
    while(!isdigit(c=getchar()));
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s;
}
bool check(ll num){
    ll l=1,r=lower_bound(cnt+1,cnt+n+1,num)-cnt,L=a[1],R=num-cnt[r-1],ans,t1,t2,now;
    for(int rt=1;rt<=n;rt++){
        while(l<rt&&r<=n){
            t1=x[rt]-x[l],t2=x[r]-x[rt];
            if(t1>t2){
                now=min(L,a[r]-R);
                L-=now;if(L==0){l++;L=a[l];}
                R+=now;if(R==a[r]){r++;R=0;}
            }else break;
        }
        ans=(cnt[rt]-cnt[l])*x[rt]-(sum[rt]-sum[l])+(x[rt]-x[l])*L+
            (sum[r-1]-sum[rt])-(cnt[r-1]-cnt[rt])*x[rt]+(x[r]-x[rt])*R;
        ans<<=1;if(ans<=T)return 1;
    }
    return 0;
}
int main(){
    n=read();T=read();
    for(int i=1;i<=n;i++)x[i]=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        cnt[i]=cnt[i-1]+a[i];
        sum[i]=sum[i-1]+a[i]*x[i];
    }
    ll l=1,r=cnt[n]+1;
    while(l<r){
        ll mid=(l+r)>>1;//mid开long long
        if(check(mid))l=mid+1;else r=mid;
    }
    printf("%lld",l-1);
    return 0;
}
View Code

 

posted @ 2017-08-11 16:08  ONION_CYC  阅读(371)  评论(0编辑  收藏  举报