[关闭]
@TryMyEdge 2017-03-07T09:33:53.000000Z 字数 5558 阅读 966

专题一(二分、三分)

题解


题目

A Strange fuction

题目大意:

    F(x)=6*x^7+8*x^6+7*x^3+5*x^2-y*x,给定y(0<y<10^10),求当0<=x<=100时F(x)的最小值。
    T(T<=100)组数据。

解题思路:

    F'(x)=42*x^6+48*x^5+21*x^2+10x-y,因为0<=x<=100,所以F'(x)是递增的。二分找到F'(x)最接近0的位置,此时的x对应F(x)的最小值。
    时间复杂度o(T*log(10^8)),空间复杂度o(1)。

AC代码:

  1. #include<iostream>
  2. #include<bits/stdc++.h>
  3. #define pq priority_queue
  4. #define Pi acos(-1.0)
  5. using namespace std;
  6. #define MOD 1000000007
  7. double y;
  8. double gg(double x)
  9. {
  10. return 42*x*x*x*x*x*x+48*x*x*x*x*x+21*x*x+10*x-y;
  11. }
  12. double ask()
  13. {
  14. double l=0,r=100;
  15. while(r-l>0.00001)
  16. {
  17. if(gg((l+r)/2)<0)
  18. l=(l+r)/2;
  19. else
  20. r=(l+r)/2;
  21. }
  22. return (l+r)/2;
  23. }
  24. int main()
  25. {
  26. int t;
  27. double x;
  28. cin>>t;
  29. while(t--)
  30. {
  31. cin>>y;
  32. x=ask();
  33. printf("%.4f\n",6*x*x*x*x*x*x*x+8*x*x*x*x*x*x+7*x*x*x+5*x*x-y*x);
  34. }
  35. return 0;
  36. }

B Can you find it?

题目大意:

    给三个长度分别为L、M、N(1<=L,N,M<=500)的数列A、B、C,然后有S(1<=S<=1000)个询问,每个询问包含一个整数X,问是否存在Ai+Bj+Ck=X。A、B、C中的数和X都为32位整数。
    多组数据,EOF。

解题思路:

    一种比较朴素的想法是把所有Ai+Bj+Ck的情况枚举出来然后对于每个询问二分查询是否存在某个组合的值为X。时间复杂度为o(L*M*N*log(L*M*N)+S*log(L*M*N)),空间复杂度为o(L*M*N),BOOM!
    我们注意到预处理的时空复杂度都很爆炸,于是我们考虑把预处理的复杂度降下来,摊分到查询中去,具体做法为把所有Ai+Bj的情况枚举出来,然后对于每个询问枚举Ck,二分查询是否存在某个组合的值为X-Ck。
    32位整数就不可能用数组打表了,多组的情况下map也会炸。。。
    时间复杂度o(L*M*log(L*M)+S*N*log(L*M)),空间复杂度o(L*M)。

AC代码:

  1. #include<iostream>
  2. #include<bits/stdc++.h>
  3. #define pq priority_queue
  4. #define Pi acos(-1.0)
  5. using namespace std;
  6. #define MOD 1000000007
  7. int numa[505],numb[505],numc[505];
  8. int nums[250005],len;
  9. int a,b,c,t=0;
  10. bool ask(int x)
  11. {
  12. int l=0,r=len-1,mid;
  13. while(l<r)
  14. {
  15. mid=(l+r)/2;
  16. if(nums[mid]<x)
  17. l=mid+1;
  18. else
  19. r=mid;
  20. }
  21. return nums[l]==x;
  22. }
  23. bool ok(int x)
  24. {
  25. for(int i=0;i<c;i++)
  26. {
  27. if(ask(x-numc[i]))
  28. return 1;
  29. }
  30. return 0;
  31. }
  32. int main()
  33. {
  34. int s,now;
  35. while(~scanf("%d%d%d",&a,&b,&c))
  36. {
  37. len=0;
  38. t++;
  39. printf("Case %d:\n",t);
  40. for(int i=0;i<a;i++)
  41. scanf("%d",numa+i);
  42. for(int i=0;i<b;i++)
  43. scanf("%d",numb+i);
  44. for(int i=0;i<c;i++)
  45. scanf("%d",numc+i);
  46. for(int i=0;i<a;i++)
  47. for(int j=0;j<b;j++)
  48. nums[len++]=numa[i]+numb[j];
  49. sort(nums,nums+len);
  50. scanf("%d",&s);
  51. for(int i=0;i<s;i++)
  52. {
  53. scanf("%d",&now);
  54. if(ok(now))
  55. printf("YES\n");
  56. else
  57. printf("NO\n");
  58. }
  59. }
  60. return 0;
  61. }

C Monthly Expense

题目大意:

    有N(1<=N<=10^5)天,第i天的花费为Pi(1<=Pi<10^4),现在将这N天分成M(1<=M<=N)段,每段包含的天数至少为1并且这些天连续,每一段的预算为这一段中的花费的总和,问预算最多的一段的预算至少为多少。

解题思路:

    当预算确定的时候,我们可以看出最优策略是当前段如果剩余预算足够支付当前天的花费就把当前天算入当前段,否则把当前天算入下一段,并且当前段结束。于是在确定预算的情况下我们可以知道需要多少段。
    二分预算,判断段数是否大于M。
    小细节:我用的是左闭右闭的二分方法,二分的左端点初值为Pmax,右端点初值为sum(Pi)。
    时间复杂度o(N*log(sum(Pi))),空间复杂度o(N)。

AC代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #define pq priority_queue
  5. #define Pi acos(-1.0)
  6. using namespace std;
  7. #define MOD 1000000007
  8. int num[100005],n,m,l=0,r=0;
  9. int ask(int x)
  10. {
  11. int now=0,ans=1;
  12. for(int i=0;i<n;i++)
  13. {
  14. if(now+num[i]<=x)
  15. now+=num[i];
  16. else
  17. {
  18. ans++;
  19. now=num[i];
  20. }
  21. }
  22. return ans;
  23. }
  24. int main()
  25. {
  26. cin>>n>>m;
  27. for(int i=0;i<n;i++)
  28. {
  29. scanf("%d",num+i);
  30. l=max(l,num[i]);
  31. r+=num[i];
  32. }
  33. int mid;
  34. while(l<r)
  35. {
  36. mid=(l+r)/2;
  37. if(ask(mid)<=m)
  38. r=mid;
  39. else
  40. l=mid+1;
  41. }
  42. cout<<l<<endl;
  43. return 0;
  44. }

D River Hopscotch

题目大意:

    有一条宽为L(1<=L<=10^9),河中有N(0<=N<=50000)块石头,每块石头离出发点的距离为Di(0<Di<L),从中移走M(0<=M<=N)块石头,问从出发点到终点,距离最短的一次跳跃最大值为多少。

解题思路:

    如果考虑移走哪些石头,那复杂度就会是指数级别的,BOOM!
    于是我们反过来考虑,如果确定了一个最短距离,那么当下一个位置和当前位置距离超过最短距离的时候就跳过去,否则就移掉一块石头,由此可以知道每个最短距离至少需要移走多少块石头才能实现。
    二分最短距离,判断需要移走的石头数是否大于M。
    依然是左闭右闭的二分:二分的左端点初值为0,右端点初值为L。
    小细节:注意把终点也加到石头序列里。
    时间复杂度o(N*log(L)),空间复杂度o(N)。

AC代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #define pq priority_queue
  6. #define Pi acos(-1.0)
  7. using namespace std;
  8. #define MOD 1000000007
  9. int l,n,m;
  10. int num[50005];
  11. int gg(int x)
  12. {
  13. int now=0,ans=0;
  14. for(int i=0;i<=n;i++)
  15. {
  16. if(num[i]-now<x)
  17. ans++;
  18. else
  19. now=num[i];
  20. }
  21. return ans;
  22. }
  23. int main()
  24. {
  25. cin>>l>>n>>m;
  26. for(int i=0;i<n;i++)
  27. scanf("%d",num+i);
  28. num[n]=l;
  29. sort(num,num+n);
  30. int lans=0,rans=l,mid;
  31. while(lans<rans)
  32. {
  33. mid=(lans+rans)/2+1;
  34. if(gg(mid)>m)
  35. rans=mid-1;
  36. else
  37. lans=mid;
  38. }
  39. cout<<lans<<endl;
  40. return 0;
  41. }

E Communication System

题目大意:

     有N(1<=N<=100)种设备,第i种设备有Mi(1<=Mi<=100)种选择,每个设备有两个属性带宽B和价格P,整套系统的总带宽为所选设备中最小的带宽值,总价格为所选设备的总价格,求总带宽除以总价格的最大值。
     B和P都是正整数,范围不超过int。
     T(T<=10)组数据。

解题思路:

    枚举带宽,在每一种设备用二分搜索能满足带宽要求的最低价格来求出总价格,更新答案。
    需要预处理两个内容:(1)需要保证枚举的某一个带宽都能有方案可以满足;(2)保证每个带宽对应的价格是满足该带宽需求的最小价格。第一点容易实现,第二点要先将每种设备的备选按照带宽从小到大,如果带宽相同价格从大到小的顺序排序,然后从后往前用更小的价格去更新前面的选择。因为后面的选择带宽一定大于等于前面的,如果带宽相同,价格又一定小于等于前面的,所以可以覆盖前面的需求。
    时间复杂度o(T*sum(Mi)*sum(log(Mi))),空间复杂度o(sum(Mi))。

AC代码:

  1. #include<iostream>
  2. //#include<bits/stdc++.h>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<algorithm>
  6. #define pq priority_queue
  7. #define Pi acos(-1.0)
  8. using namespace std;
  9. int m[105],n;
  10. struct Prod
  11. {
  12. int b,p;
  13. }pr[105][105];
  14. int num[10005];
  15. bool cmp(Prod x1,Prod x2)
  16. {
  17. if(x1.b==x2.b)
  18. return x1.p>x2.p;
  19. else
  20. return x1.b<x2.b;
  21. }
  22. double ask(int x)
  23. {
  24. double now=0;
  25. int l,r,mid;
  26. for(int i=1;i<=n;i++)
  27. {
  28. l=1;
  29. r=m[i];
  30. while(l!=r)
  31. {
  32. mid=(l+r)/2;
  33. if(pr[i][mid].b>=x)
  34. r=mid;
  35. else
  36. l=mid+1;
  37. }
  38. now+=pr[i][l].p;
  39. }
  40. return x/now;
  41. }
  42. int main()
  43. {
  44. int t,temp,rr,len;
  45. double ans;
  46. cin>>t;
  47. while(t--)
  48. {
  49. ans=0;
  50. cin>>n;
  51. for(int i=1;i<=n;i++)
  52. {
  53. scanf("%d",m+i);
  54. temp=0;
  55. for(int j=1;j<=m[i];j++)
  56. {
  57. scanf("%d%d",&pr[i][j].b,&pr[i][j].p);
  58. temp=max(temp,pr[i][j].b);
  59. }
  60. if(i>1)
  61. rr=min(rr,temp);
  62. else
  63. rr=temp;
  64. }
  65. len=0;
  66. for(int i=1;i<=n;i++)
  67. {
  68. for(int j=1;j<=m[i];j++)
  69. if(pr[i][j].b<=rr)
  70. num[len++]=pr[i][j].b;
  71. sort(pr[i]+1,pr[i]+1+m[i],cmp);
  72. for(int j=m[i]-1;j;j--)
  73. if(pr[i][j].p>pr[i][j+1].p)
  74. pr[i][j].p=pr[i][j+1].p;
  75. }
  76. sort(num,num+len);
  77. for(int i=0;i<len;i++)
  78. ans=max(ans,ask(num[i]));
  79. printf("%.3f\n",ans);
  80. }
  81. return 0;
  82. }

F Light Bulb

题目大意:

一个身高为h(0.01<=h<=1000)的人站在房子中,他左边有一盏高度为H(0.01+h<=H<=1000)的灯,右边有一面墙,灯和墙的水平距离为D(0.01<=D<=1000),问这个人的影子的长度最长为多少。
T(T<=100)组数据。

解题思路:

    这题其实可以直接推公式求最值2333333
    给定人和灯的水平距离,分影子全在地上和有一部分在墙上,可以算出影子的长度。
    影子长度关于人灯距离的图像是一个凸型函数,所以可以三分人灯距离。
    小细节:注意结束条件的设置。
    时间复杂度o(T*log(D*10^7)),空间复杂度o(1)。

AC代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<algorithm>
  5. #define pq priority_queue
  6. #define Pi acos(-1.0)
  7. using namespace std;
  8. double H,h,D;
  9. double ask(double x)
  10. {
  11. return D+H-x-D*(H-h)/x;
  12. }
  13. int main()
  14. {
  15. int t;
  16. cin>>t;
  17. double l,r,mid1,mid2;
  18. while(t--)
  19. {
  20. cin>>H>>h>>D;
  21. l=(H-h)*D/H;
  22. r=D;
  23. while(r-l>0.0000001)
  24. {
  25. mid1=l+(r-l)/3;
  26. mid2=l+r-mid1;
  27. if(ask(mid1)<ask(mid2))
  28. l=mid1;
  29. else
  30. r=mid2;
  31. }
  32. printf("%.3f\n",max(ask(l),ask(r)));
  33. }
  34. return 0;
  35. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注