Codeforces 609D 被二分教做人
2015-12-24 09:49
381 查看
传送门:http://codeforces.com/problemset/problem/609/D
(如需转载,请注明出处,谢谢O(∩_∩)O)
题意:
Nura想买k个小玩意,她手上有 s 个burles(一种货币),有m个小玩意供她选择购买,但每个小玩意只能用dollars或者pounds来购买,所以每次购买的时候Nura都要通过汇率将她手上的burles换成dollars或者pounds,而每一天的汇率又不一样,给出n天,并且这n天中burles换成dollars和pounds的比率,问是否能在这n天中用手上的burles买下k个小玩意,如果不能,输出-1;如果能,输出最小的天数(即能使得Nura买下k个小玩意的最少天数),并在接下来的m行中,依次输出两个数字,分别是买下的小玩意的编号(按给出的顺序编号)以及买下该小玩意的日期(即第几天)。——在同一天中可以买任意多个小玩意。
解题思路与分析:
注意到数据范围是2e5,而显然需要通过某种方式来比较“天数”,直接枚举肯定很虚,所以要用到二分。
(这里给出同学写的二分的正确姿势:
int L,R,M;
while(R-L>1){
M=((R-L)>>1)+L;
if() R=M;
else L=M;
}
)
确定了用二分来枚举天数之后,就需要确定二分里面的内容。因为一天可以买多个,所以显然应该找兑换率最高的一天,即区间最小值,这里用了ST算法,O(nlogn)预处理,O(1)查询——获得了区间里burles兑换dollars和pounds最“划算”的一天后,就是确定买下k个小玩意的最小花费了,这里没有想到更简便的方法,只能用了O(mlogm)的预处理(排序),每次O(k)获取花费。用结构体存储小玩意的type,cost以及num(编号,后面输出用到),然后排序——重载小于号,先按type排,再按cost排,记录type==2的第一个小玩意对应的下标ter,之后只需要每次计算的时候比较一下,就能保证获取最小花费了(细节见代码)——获取最小花费后,将之与s比较一下即可知该区间是否可行。
代码实现过程与反思:
感觉基本上坑被踩了个尽。。
1、计算最小花费的时候,会造成int溢出,要用long long。
2、不是坑的坑:原题output部分中的”number of gadget”,是指小玩意的编号,不是数目(gadget没有s),醉。
4、细节:计算最小花费的时候,while循环应为j<m而不为j<n(j为ter为起点的下标,i从0开始),又wa一发。。
3、二分的时候,因为while中的是R-L>1,所以当n为1时,将直接跳过二分判断。并且,如果存在可行的答案,其必然为二分循环结束后的L或者R,先判L,如果L不行就判断R;如果都不行,说明不存在正确结果。这里又wa了几发,,被二分教做人。
代码如下:
View Code
(如需转载,请注明出处,谢谢O(∩_∩)O)
题意:
Nura想买k个小玩意,她手上有 s 个burles(一种货币),有m个小玩意供她选择购买,但每个小玩意只能用dollars或者pounds来购买,所以每次购买的时候Nura都要通过汇率将她手上的burles换成dollars或者pounds,而每一天的汇率又不一样,给出n天,并且这n天中burles换成dollars和pounds的比率,问是否能在这n天中用手上的burles买下k个小玩意,如果不能,输出-1;如果能,输出最小的天数(即能使得Nura买下k个小玩意的最少天数),并在接下来的m行中,依次输出两个数字,分别是买下的小玩意的编号(按给出的顺序编号)以及买下该小玩意的日期(即第几天)。——在同一天中可以买任意多个小玩意。
解题思路与分析:
注意到数据范围是2e5,而显然需要通过某种方式来比较“天数”,直接枚举肯定很虚,所以要用到二分。
(这里给出同学写的二分的正确姿势:
int L,R,M;
while(R-L>1){
M=((R-L)>>1)+L;
if() R=M;
else L=M;
}
)
确定了用二分来枚举天数之后,就需要确定二分里面的内容。因为一天可以买多个,所以显然应该找兑换率最高的一天,即区间最小值,这里用了ST算法,O(nlogn)预处理,O(1)查询——获得了区间里burles兑换dollars和pounds最“划算”的一天后,就是确定买下k个小玩意的最小花费了,这里没有想到更简便的方法,只能用了O(mlogm)的预处理(排序),每次O(k)获取花费。用结构体存储小玩意的type,cost以及num(编号,后面输出用到),然后排序——重载小于号,先按type排,再按cost排,记录type==2的第一个小玩意对应的下标ter,之后只需要每次计算的时候比较一下,就能保证获取最小花费了(细节见代码)——获取最小花费后,将之与s比较一下即可知该区间是否可行。
代码实现过程与反思:
感觉基本上坑被踩了个尽。。
1、计算最小花费的时候,会造成int溢出,要用long long。
2、不是坑的坑:原题output部分中的”number of gadget”,是指小玩意的编号,不是数目(gadget没有s),醉。
4、细节:计算最小花费的时候,while循环应为j<m而不为j<n(j为ter为起点的下标,i从0开始),又wa一发。。
3、二分的时候,因为while中的是R-L>1,所以当n为1时,将直接跳过二分判断。并且,如果存在可行的答案,其必然为二分循环结束后的L或者R,先判L,如果L不行就判断R;如果都不行,说明不存在正确结果。这里又wa了几发,,被二分教做人。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int N=2e5+10,M=15; struct gadget{ int type,cost,num; bool operator < (const gadget g) const{ return type==g.type?cost<g.cost:type<g.type; } }G ; int n,m,k,s,ter,a ,b ,da [M],db [M]; void init(){ for(int i=0;i<n;i++) da[i][0]=a[i],db[i][0]=b[i]; for(int j=1;(1<<j)<=n;j++) for(int i=0;i+(1<<j)-1<n;i++){ da[i][j]=min(da[i][j-1],da[i+(1<<(j-1))][j-1]); db[i][j]=min(db[i][j-1],db[i+(1<<(j-1))][j-1]); } } int RMQ(int L,int R,int d [M]){ int k=0; while((1<<(k+1))<=R-L+1) k++; return min(d[L][k],d[R-(1<<k)+1][k]); } bool check(ll p1,ll p2){ int cnt=0; ll tot=0; int i=0,j=ter; while(j<m){ if(i==ter||cnt==k) break; if(G[i].cost*p1<=G[j].cost*p2) tot+=G[i++].cost*p1; else tot+=G[j++].cost*p2; cnt++; } if(cnt<k){ if(i<ter) swap(i,j),swap(p1,p2); while(cnt<k) tot+=p2*G[j++].cost,cnt++; } return tot<=s; } int main(){ //freopen("in.txt","r",stdin); while(~scanf("%d%d%d%d",&n,&m,&k,&s)){ for(int i=0;i<n;i++) scanf("%d",a+i); for(int i=0;i<n;i++) scanf("%d",b+i); ter=0; for(int i=0;i<m;i++){ scanf("%d%d",&G[i].type,&G[i].cost); if(G[i].type==1) ter++; G[i].num=i+1; } init(); sort(G,G+m); bool flag=false; int L=0,R=n-1,mid; ll p1,p2; while(R-L>1){ mid=((R-L)>>1)+L; p1=RMQ(0,mid,da),p2=RMQ(0,mid,db); if(check(p1,p2)) R=mid; else L=mid; } p1=RMQ(0,L,da),p2=RMQ(0,L,db); if(check(p1,p2)) flag=true; else{ p1=RMQ(0,R,da),p2=RMQ(0,R,db); if(check(p1,p2)) flag=true; } if(flag){ int d1,d2; for(int i=0;i<n;i++) if(a[i]==p1){ d1=i+1;break; } for(int i=0;i<n;i++) if(b[i]==p2){ d2=i+1;break; } printf("%d\n",max(d1,d2)); int cnt=0,tot=0; int i=0,j=ter; while(j<m){ if(i==ter||cnt==k) break; if(G[i].cost*p1<=G[j].cost*p2){ cnt++; printf("%d %d\n",G[i++].num,d1); } else{ cnt++; printf("%d %d\n",G[j++].num,d2); } } if(cnt<k){ if(i<ter) swap(d1,d2),swap(i,j); while(cnt<k) printf("%d %d\n",G[j++].num,d2),cnt++; } } else puts("-1"); } return 0; }
View Code
相关文章推荐
- 创建单例
- PAT-逆序的三位数(简单编程题)
- 按返回键退出程序
- IOS 简单的动画自定义方法(旋转、移动、闪烁等)
- linux之sed用法
- Java BufferedInputStream与BufferedOutputStream 入门版实例解析【文件操作】
- 01--好的json解析
- 转载——验证码的昨天、今天和明天
- alert和console.log调试的区别
- 震惊!黄光裕、李嘉诚、马化腾甚至股市中的“野蛮人”都出自潮汕帮(附股)
- html中超链接颜色改变,实际项目可能用到
- netty之事件驱动原理
- 个人杂记-切换gcc版本-make某些错误
- JUC包之AbstractQueuedSynchronizer源码学习
- 【蓝桥杯】【运送马匹】
- Behavioral模式之Observer模式
- Leetcode: Closest Binary Search Tree Value
- <php+mysql>PHP基础,使用PHP访问表单数据
- Java守护线程概述
- Excel的VBA连接数据库方法