• (2022杭电多校三)1011.Taxi(曼哈顿最值+二分)


    题目:

    样例输入:

    1. 1
    2. 3 4
    3. 1 5 7
    4. 5 1 6
    5. 2 3 9
    6. 1 5
    7. 2 2
    8. 4 3
    9. 10 10

    样例输出:

    1. 6
    2. 4
    3. 5
    4. 9

    题意:多组样例,对于每组样例,先给出一个n和m,n代表点的个数,m代表询问的个数,接下来n行,每行3个数(xi,yi,wi),分别代表第i个点的坐标和权值,对于每组询问,首先给出一个坐标,让我们求出这个点到n个点中的值的最大值,这个点到第i个点的值定义为两点曼哈顿距离和i点权值的较小值。

    分析:我们需要先来看一下如何求一个点到n个点的曼哈顿距离的最大值,这是一个比较经典的题目,我直接在纸上给出讲解吧:

    其实就是维护一个n个点的(x+y)和(x-y)的最大值和最小值,然后我们就可以O(1)来求出这个点到n个点的曼哈顿距离的最大值,最小值也是以同样的方式求得,只是把上面的max改成min即可,这个模型过于经典,希望大家牢牢记住!

    这道题目还加上了w这个限制,我们可以先按照w对点进行从小到大排序。然后就可以二分求解了。

    下面我以两种不同的方式来进行二分:

    (1)二分答案:

    我们直接二分答案,比如我们想要判断答案是不是大于等于x,那么我们先找到第一个权值大于等于x的点,那么编号比这个点小的点是不可能得到大于x的解的,因为权值小于x,而编号比这个点大的点的权值都是大于等于x的,我们可以求出当前编号及之后所有点到询问点的曼哈顿距离的最大值(这个可以倒序遍历一遍预处理得到),如果最大值大于等于x,那么x就是可以的,因为最大值大于等于x说明存在一个点权大于等于x且到询问点曼哈顿距离也大于等于x的点,所以答案一定大于等于x,返回true即可,如果不存在,那么答案一定小于x,因为我们取的是曼哈顿距离和点权的较小值,点权大于x的点的最大曼哈顿距离都小于x了,那么答案一定要小于x,返回false,这样就可以二分套二分得到答案(因为在check函数中求解第一个点权大于等于x的过程也是一个二分过程)

    所以这种方法总的复杂度就是n(logn)*(logn)

    下面是代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. const int N=1e5+10;
    11. struct node{
    12. int x,y,w;
    13. }p[N];
    14. bool cmp(node a,node b)
    15. {
    16. return a.w
    17. }
    18. int mx1[N],mx2[N],mn1[N],mn2[N];
    19. //mx1[i]记录排完序的p[i~n]数组中x+y的最大值
    20. //mx2[i]记录排完序的p[i~n]数组中x-y的最大值
    21. //mn1[i]记录排完序的p[i~n]数组中x+y的最小值
    22. //mn2[i]记录排完序的p[i~n]数组中x-y的最小值
    23. int n,m,qx,qy;
    24. bool check(int x)
    25. {
    26. int l=1,r=n;
    27. while(l
    28. {
    29. int mid=l+r>>1;
    30. if(p[mid].w>=x) r=mid;
    31. else l=mid+1;
    32. }
    33. if(p[l].wreturn false;
    34. int d=qx+qy-mn1[l];
    35. d=max(d,qx-qy-mn2[l]);
    36. d=max(d,-qx+qy+mx2[l]);
    37. d=max(d,-qx-qy+mx1[l]);
    38. return d>=x;
    39. }
    40. int main()
    41. {
    42. int T;
    43. cin>>T;
    44. while(T--)
    45. {
    46. scanf("%d%d",&n,&m);
    47. for(int i=1;i<=n;i++)
    48. scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w);
    49. sort(p+1,p+n+1,cmp);
    50. mx1[n]=mn1[n]=p[n].x+p[n].y;
    51. mx2[n]=mn2[n]=p[n].x-p[n].y;
    52. for(int i=n-1;i>=1;i--)
    53. {
    54. mx1[i]=max(mx1[i+1],p[i].x+p[i].y);
    55. mx2[i]=max(mx2[i+1],p[i].x-p[i].y);
    56. mn1[i]=min(mn1[i+1],p[i].x+p[i].y);
    57. mn2[i]=min(mn2[i+1],p[i].x-p[i].y);
    58. }
    59. while(m--)
    60. {
    61. scanf("%d%d",&qx,&qy);
    62. int l=1,r=1e9;
    63. while(l
    64. {
    65. int mid=l+r+1>>1;
    66. if(check(mid)) l=mid;
    67. else r=mid-1;
    68. }
    69. printf("%d\n",l);
    70. }
    71. }
    72. return 0;
    73. }

    (2)二分城镇

    我们用ans记录最后的答案

    我们考虑第x个城镇,首先我们知道它的点权为p[x].w,求出编号大于等于x的城镇中到询问点的最大曼哈顿距离d

    如果d>=p[x].w,那么答案一定是大于p[x].w的,分析同上,接下来我们就可以二分位于[k,n]的这些城镇,同时在过程中记录答案的最大值。

    如果d

    这种方法的复杂度是O(nlogn),要好于第一种方法的复杂度。

    下面是代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. const int N=1e5+10;
    11. struct node{
    12. int x,y,w;
    13. }p[N];
    14. bool cmp(node a,node b)
    15. {
    16. return a.w
    17. }
    18. int mx1[N],mx2[N],mn1[N],mn2[N];
    19. //mx1[i]记录排完序的p[i~n]数组中x+y的最大值
    20. //mx2[i]记录排完序的p[i~n]数组中x-y的最大值
    21. //mn1[i]记录排完序的p[i~n]数组中x+y的最小值
    22. //mn2[i]记录排完序的p[i~n]数组中x-y的最小值
    23. int n,m,qx,qy,ans;
    24. bool check(int x)
    25. {
    26. int d=qx+qy-mn1[x];
    27. d=max(d,qx-qy-mn2[x]);
    28. d=max(d,-qx+qy+mx2[x]);
    29. d=max(d,-qx-qy+mx1[x]);
    30. if(d>=p[x].w)//对答案的贡献是p[x].w
    31. {
    32. ans=max(ans,p[x].w);
    33. return true;
    34. }
    35. else//对答案的贡献是d
    36. {
    37. ans=max(ans,d);
    38. return false;
    39. }
    40. }
    41. int main()
    42. {
    43. int T;
    44. cin>>T;
    45. while(T--)
    46. {
    47. scanf("%d%d",&n,&m);
    48. for(int i=1;i<=n;i++)
    49. scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w);
    50. sort(p+1,p+n+1,cmp);
    51. mx1[n]=mn1[n]=p[n].x+p[n].y;
    52. mx2[n]=mn2[n]=p[n].x-p[n].y;
    53. for(int i=n-1;i>=1;i--)
    54. {
    55. mx1[i]=max(mx1[i+1],p[i].x+p[i].y);
    56. mx2[i]=max(mx2[i+1],p[i].x-p[i].y);
    57. mn1[i]=min(mn1[i+1],p[i].x+p[i].y);
    58. mn2[i]=min(mn2[i+1],p[i].x-p[i].y);
    59. }
    60. while(m--)
    61. {
    62. scanf("%d%d",&qx,&qy);
    63. ans=0;
    64. int l=0,r=n;
    65. while(l
    66. {
    67. int mid=l+r+1>>1;
    68. if(check(mid)) l=mid;
    69. else r=mid-1;
    70. }
    71. printf("%d\n",ans);
    72. }
    73. }
    74. return 0;
    75. }

  • 相关阅读:
    京源转债上市价格预测
    如何理解高效IO
    剑指 Offer 48. 最长不含重复字符的子字符串
    大数据培训课程GroupingComparator分组案例实操
    Python 集合13 update()方法—更新为并集
    SMK电路板USBType-C插座CSS5324-3M01F CSS5324-0M02F连接器以及过压保护
    XTU-OJ 1175-Change
    前端--HTML
    指针进阶(3)
    1391:局域网(net)——Prim、Kruskal 最小生成树
  • 原文地址:https://blog.csdn.net/AC__dream/article/details/126002673