uoj #49. [UR #3] 铀仓库

题目链接

题意:数轴上某些点有若干物品,任选起点在规定时间T内移动到附近将一个物品取回起点。移动单位距离的时间代价为1,取物品不需付出时间,求最多保留的物品

算法:二分答案+贪心判断

知道做法之后思路是很清晰的,难的是想出正解还有实现时处理好各种细节

原比赛题解

CYC题解

二分答案之后将求值转化为判断

几个问题:

1.如何判断:在时间T内,能否取到x的值(物品)

易得取的必是连续的一段区间

从左到右枚举起点,易知起点离左边越来越远,离右边越来越近,则该区间一定是单调不下降的

所以每次移动起点,就贪心地比较左右边界(到起点的距离)。

若右边更优,则舍弃左边;直到左边界比右边界更优,此时如果再去移动区间的话,就变成了用左边的优值去交换右边的差值,继续向右拓展只会取到更劣的解。

这样对于每个起点(x=p),就能O(p)地找到最优的区间

2.记录区间的起点、左右边界之后,如何O(1)地求出付出的时间代价

需要简单的数学技巧。

读入时维护a数组的前缀和cnt,以及a*x的前缀和sum

sum数组是解决问题的关键,其数学意义即为在求从起点往位置x[i]移动时,需要付出的代价即为(取的个数*距离)

经过数学变换可推出

cost=(cnt[st]-cnt[l])*x[st]-(sum[st]-sum[l])+l_num*(x[st]-x[l])+r_num*(x[r]-x[st])-(cnt[r-1]-cnt[st])*x[st]+(sum[r-1]-sum[st]);

1 cost=(cnt[st]-cnt[l])*x[st]-(sum[st]-sum[l])+l_num*(x[st]-x[l])+     //Left
2       r_num*(x[r]-x[st])-(cnt[r-1]-cnt[st])*x[st]+(sum[r-1]-sum[st]);//Right
3 对于左半区间:用区间内权值的和*起点坐标,利用sum数组减去多余的值;
4               左端点的代价可以直接求出
5 右半区间同理。 
简单说明?其实是我数学差

最后由于往返,代价*2

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cctype>
 4 #include<algorithm>
 5 using namespace std;
 6 #define maxn 500010
 7 #define ll long long
 8 ll n,t;
 9 ll cnt[maxn],sum[maxn],x[maxn],a[maxn];
10 ll read(){
11     ll x=0;char ch=getchar();
12     while (!isdigit(ch)){ch=getchar();}
13     while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
14     return x;
15 }
16 void init(){
17     n=read(),t=read();
18     for (int i=1;i<=n;i++) x[i]=read();
19     for (int i=1;i<=n;i++) a[i]=read();
20     for (int i=1;i<=n;i++) cnt[i]=cnt[i-1]+a[i],sum[i]=sum[i-1]+a[i]*x[i];
21 }
22 bool ok(int goal){
23     ll l=1,r=lower_bound(cnt+1,cnt+n+1,goal)-cnt,l_num=a[1],r_num=goal-cnt[r-1],d1,d2,now,cost;
24     for (int st=1;st<=n;st++){
25         while (l<st&&r<=n){
26             int d1=x[st]-x[l],d2=x[r]-x[st];
27             if (d1>d2){
28                 now=min(l_num,a[r]-r_num);
29                 l_num-=now,r_num+=now;
30                 if (l_num==0) l++,l_num=a[l];
31                 if (r_num==a[r]) r++,r_num=0;
32             }else break;
33         }
34         cost=(cnt[st]-cnt[l])*x[st]-(sum[st]-sum[l])+l_num*(x[st]-x[l])
35              +r_num*(x[r]-x[st])-(cnt[r-1]-cnt[st])*x[st]+(sum[r-1]-sum[st]);
36         cost<<=1;if (cost<=t) return 1;
37     }             
38     return 0;
39 }               
40 int main(){
41     init();
42     ll left=0,right=cnt[n]+1,mid=(left+right)>>2;
43     while (left<right){
44         mid=(left+right)>>1;
45         if (ok(mid)) left=mid+1;
46         else right=mid;
47     }
48     printf("%lld\n",left-1);
49     return 0;
50 }
View Code

 

posted @ 2017-08-13 19:30  Vincent_hwh  阅读(200)  评论(0编辑  收藏  举报