【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; }