第十七节.坐标范围选择

Projected Coordinate System 投影坐标
EPSG    投影坐标系名称    经度最小    经度最大    中央经线    备注
4513    CGCS2000 / 3-degree Gauss-Kruger zone 25    73.5    76.5    75    加代号
4514    CGCS2000 / 3-degree Gauss-Kruger zone 26    76.5    79.5    78    加代号
4515    CGCS2000 / 3-degree Gauss-Kruger zone 27    79.5    82.5    81    加代号
4516    CGCS2000 / 3-degree Gauss-Kruger zone 28    82.5    85.5    84    加代号
4517    CGCS2000 / 3-degree Gauss-Kruger zone 29    85.5    88.5    87    加代号
4518    CGCS2000 / 3-degree Gauss-Kruger zone 30    88.5    91.5    90    加代号
4519    CGCS2000 / 3-degree Gauss-Kruger zone 31    91.5    94.5    93    加代号
4520    CGCS2000 / 3-degree Gauss-Kruger zone 32    94.5    97.5    96    加代号
4521    CGCS2000 / 3-degree Gauss-Kruger zone 33    97.5    100.5    99    加代号
4522    CGCS2000 / 3-degree Gauss-Kruger zone 34    100.5    103.5    102    加代号
4523    CGCS2000 / 3-degree Gauss-Kruger zone 35    103.5    106.5    105    加代号
4524    CGCS2000 / 3-degree Gauss-Kruger zone 36    106.5    109.5    108    加代号
4525    CGCS2000 / 3-degree Gauss-Kruger zone 37    109.5    112.5    111    加代号
4526    CGCS2000 / 3-degree Gauss-Kruger zone 38    112.5    115.5    114    加代号
4527    CGCS2000 / 3-degree Gauss-Kruger zone 39    115.5    118.5    117    加代号
4528    CGCS2000 / 3-degree Gauss-Kruger zone 40    118.5    121.5    120    加代号
4529    CGCS2000 / 3-degree Gauss-Kruger zone 41    121.5    124.5    123    加代号
4530    CGCS2000 / 3-degree Gauss-Kruger zone 42    124.5    127.5    126    加代号
4531    CGCS2000 / 3-degree Gauss-Kruger zone 43    127.5    130.5    129    加代号
4532    CGCS2000 / 3-degree Gauss-Kruger zone 44    130.5    133.5    132    加代号
4533    CGCS2000 / 3-degree Gauss-Kruger zone 45    133.5    136.5    135    加代号
4534    CGCS2000 / 3-degree Gauss-Kruger CM 75E    73.5    76.5    75    
4535    CGCS2000 / 3-degree Gauss-Kruger CM 78E    76.5    79.5    78    
4536    CGCS2000 / 3-degree Gauss-Kruger CM 81E    79.5    82.5    81    
4537    CGCS2000 / 3-degree Gauss-Kruger CM 84E    82.5    85.5    84    
4538    CGCS2000 / 3-degree Gauss-Kruger CM 87E    85.5    88.5    87    
4539    CGCS2000 / 3-degree Gauss-Kruger CM 90E    88.5    91.5    90    
4540    CGCS2000 / 3-degree Gauss-Kruger CM 93E    91.5    94.5    93    
4541    CGCS2000 / 3-degree Gauss-Kruger CM 96E    94.5    97.5    96    
4542    CGCS2000 / 3-degree Gauss-Kruger CM 99E    97.5    100.5    99    
4543    CGCS2000 / 3-degree Gauss-Kruger CM 102E    100.5    103.5    102    
4544    CGCS2000 / 3-degree Gauss-Kruger CM 105E    103.5    106.5    105    
4545    CGCS2000 / 3-degree Gauss-Kruger CM 108E    106.5    109.5    108    
4546    CGCS2000 / 3-degree Gauss-Kruger CM 111E    109.5    112.5    111    
4547    CGCS2000 / 3-degree Gauss-Kruger CM 114E    112.5    115.5    114    
4548    CGCS2000 / 3-degree Gauss-Kruger CM 117E    115.5    118.5    117    
4549    CGCS2000 / 3-degree Gauss-Kruger CM 120E    118.5    121.5    120    
4550    CGCS2000 / 3-degree Gauss-Kruger CM 123E    121.5    124.5    123    
4551    CGCS2000 / 3-degree Gauss-Kruger CM 126E    124.5    127.5    126    
4552    CGCS2000 / 3-degree Gauss-Kruger CM 129E    127.5    130.5    129    
4553    CGCS2000 / 3-degree Gauss-Kruger CM 132E    130.5    133.5    132    
4554    CGCS2000 / 3-degree Gauss-Kruger CM 135E    133.5    136.5    135    
2401    Beijing_1954_3_Degree_GK_Zone_25    73.5    76.5    75    加代号
2402    Beijing_1954_3_Degree_GK_Zone_26    76.5    79.5    78    加代号
2403    Beijing_1954_3_Degree_GK_Zone_27    79.5    82.5    81    加代号
2404    Beijing_1954_3_Degree_GK_Zone_28    82.5    85.5    84    加代号
2405    Beijing_1954_3_Degree_GK_Zone_29    85.5    88.5    87    加代号
2406    Beijing_1954_3_Degree_GK_Zone_30    88.5    91.5    90    加代号
2407    Beijing_1954_3_Degree_GK_Zone_31    91.5    94.5    93    加代号
2408    Beijing_1954_3_Degree_GK_Zone_32    94.5    97.5    96    加代号
2409    Beijing_1954_3_Degree_GK_Zone_33    97.5    100.5    99    加代号
2410    Beijing_1954_3_Degree_GK_Zone_34    100.5    103.5    102    加代号
2411    Beijing_1954_3_Degree_GK_Zone_35    103.5    106.5    105    加代号
2412    Beijing_1954_3_Degree_GK_Zone_36    106.5    109.5    108    加代号
2413    Beijing_1954_3_Degree_GK_Zone_37    109.5    112.5    111    加代号
2414    Beijing_1954_3_Degree_GK_Zone_38    112.5    115.5    114    加代号
2415    Beijing_1954_3_Degree_GK_Zone_39    115.5    118.5    117    加代号
2416    Beijing_1954_3_Degree_GK_Zone_40    118.5    121.5    120    加代号
2417    Beijing_1954_3_Degree_GK_Zone_41    121.5    124.5    123    加代号
2418    Beijing_1954_3_Degree_GK_Zone_42    124.5    127.5    126    加代号
2419    Beijing_1954_3_Degree_GK_Zone_43    127.5    130.5    129    加代号
2420    Beijing_1954_3_Degree_GK_Zone_44    130.5    133.5    132    加代号
2421    Beijing_1954_3_Degree_GK_Zone_45    133.5    136.5    135    加代号
2422    Beijing_1954_3_Degree_GK_CM_75E    73.5    76.5    75    
2423    Beijing_1954_3_Degree_GK_CM_78E    76.5    79.5    78    
2424    Beijing_1954_3_Degree_GK_CM_81E    79.5    82.5    81    
2425    Beijing_1954_3_Degree_GK_CM_84E    82.5    85.5    84    
2426    Beijing_1954_3_Degree_GK_CM_87E    85.5    88.5    87    
2427    Beijing_1954_3_Degree_GK_CM_90E    88.5    91.5    90    
2428    Beijing_1954_3_Degree_GK_CM_93E    91.5    94.5    93    
2429    Beijing_1954_3_Degree_GK_CM_96E    94.5    97.5    96    
2430    Beijing_1954_3_Degree_GK_CM_99E    97.5    100.5    99    
2431    Beijing_1954_3_Degree_GK_CM_102E    100.5    103.5    102    
2432    Beijing_1954_3_Degree_GK_CM_105E    103.5    106.5    105    
2433    Beijing_1954_3_Degree_GK_CM_108E    106.5    109.5    108    
2434    Beijing_1954_3_Degree_GK_CM_111E    109.5    112.5    111    
2435    Beijing_1954_3_Degree_GK_CM_114E    112.5    115.5    114    
2436    Beijing_1954_3_Degree_GK_CM_117E    115.5    118.5    117    
2437    Beijing_1954_3_Degree_GK_CM_120E    118.5    124.5    120    
2438    Beijing_1954_3_Degree_GK_CM_123E    121.5    121.5    123    
2439    Beijing_1954_3_Degree_GK_CM_126E    124.5    127.5    126    
2440    Beijing_1954_3_Degree_GK_CM_129E    127.5    130.5    129    
2441    Beijing_1954_3_Degree_GK_CM_132E    130.5    133.5    132    
2442    Beijing_1954_3_Degree_GK_CM_135E    133.5    136.5    135    
2349    Xian_1980_3_Degree_GK_Zone_25    73.5    76.5    75    加代号
2350    Xian_1980_3_Degree_GK_Zone_26    76.5    79.5    78    加代号
2351    Xian_1980_3_Degree_GK_Zone_27    79.5    82.5    81    加代号
2352    Xian_1980_3_Degree_GK_Zone_28    82.5    85.5    84    加代号
2353    Xian_1980_3_Degree_GK_Zone_29    85.5    88.5    87    加代号
2354    Xian_1980_3_Degree_GK_Zone_30    88.5    91.5    90    加代号
2355    Xian_1980_3_Degree_GK_Zone_31    91.5    94.5    93    加代号
2356    Xian_1980_3_Degree_GK_Zone_32    94.5    97.5    96    加代号
2357    Xian_1980_3_Degree_GK_Zone_33    97.5    100.5    99    加代号
2358    Xian_1980_3_Degree_GK_Zone_34    100.5    103.5    102    加代号
2359    Xian_1980_3_Degree_GK_Zone_35    103.5    106.5    105    加代号
2360    Xian_1980_3_Degree_GK_Zone_36    106.5    109.5    108    加代号
2361    Xian_1980_3_Degree_GK_Zone_37    109.5    112.5    111    加代号
2362    Xian_1980_3_Degree_GK_Zone_38    112.5    115.5    114    加代号
2363    Xian_1980_3_Degree_GK_Zone_39    115.5    118.5    117    加代号
2364    Xian_1980_3_Degree_GK_Zone_40    118.5    121.5    120    加代号
2365    Xian_1980_3_Degree_GK_Zone_41    121.5    124.5    123    加代号
2366    Xian_1980_3_Degree_GK_Zone_42    124.5    127.5    126    加代号
2367    Xian_1980_3_Degree_GK_Zone_43    127.5    130.5    129    加代号
2368    Xian_1980_3_Degree_GK_Zone_44    130.5    133.5    132    加代号
2369    Xian_1980_3_Degree_GK_Zone_45    133.5    136.5    135    加代号
2370    Xian_1980_3_Degree_GK_CM_75E    73.5    76.5    75    
2371    Xian_1980_3_Degree_GK_CM_78E    76.5    79.5    78    
2372    Xian_1980_3_Degree_GK_CM_81E    79.5    82.5    81    
2373    Xian_1980_3_Degree_GK_CM_84E    82.5    85.5    84    
2374    Xian_1980_3_Degree_GK_CM_87E    85.5    88.5    87    
2375    Xian_1980_3_Degree_GK_CM_90E    88.5    91.5    90    
2376    Xian_1980_3_Degree_GK_CM_93E    91.5    94.5    93    
2377    Xian_1980_3_Degree_GK_CM_96E    94.5    97.5    96    
2378    Xian_1980_3_Degree_GK_CM_99E    97.5    100.5    99    
2379    Xian_1980_3_Degree_GK_CM_102E    100.5    103.5    102    
2380    Xian_1980_3_Degree_GK_CM_105E    103.5    106.5    105    
2381    Xian_1980_3_Degree_GK_CM_108E    106.5    109.5    108    
2382    Xian_1980_3_Degree_GK_CM_111E    109.5    112.5    111    
2383    Xian_1980_3_Degree_GK_CM_114E    112.5    115.5    114    
2384    Xian_1980_3_Degree_GK_CM_117E    115.5    118.5    117    
2385    Xian_1980_3_Degree_GK_CM_120E    118.5    121.5    120    
2386    Xian_1980_3_Degree_GK_CM_123E    121.5    124.5    123    
2387    Xian_1980_3_Degree_GK_CM_126E    124.5    127.5    126    
2388    Xian_1980_3_Degree_GK_CM_129E    127.5    130.5    129    
2389    Xian_1980_3_Degree_GK_CM_132E    130.5    133.5    132    
2390    Xian_1980_3_Degree_GK_CM_135E    133.5    136.5    135    
高斯-克吕格3度带与6度带投影图

参考链接
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/qq_41441896/article/details/104525296/

<think>嗯,我现在要优化这个代码的时间复杂度。首先,我需要理解原代码的功能和结构。原代码是处理一个完全m叉树,给定n个点,求某个点k的子点数目。看起来用的是广度优先搜索(BFS)的方式,每次将当前点的所有子点入队,然后统计总的点数目。这样时间复杂度可能比较高,尤其是当树很大时,BFS需要遍历很多点,导致时间不够。 那原代码的时间复杂度是多少呢?假设每次处理一个点,每个点有m个子点,那么最坏情况下,如果整个树都是k的子树,那么点数可能是O(m^d),其中d是树的深度。这样时间复杂度是O(m^d),这显然对于大的m或者d来说,会很慢。比如,当m=1000,d=10的话,就是1000^10次操作,这显然不可行。所以需要找到更优的算法,将时间复杂度降低到O(1)或者O(log n)之类的。 那问题转化为,如何在不遍历的情况下计算以k为根的子树的点数。这可能涉及到完全m叉树的结构分析。 首先,我需要回忆完全m叉树的结构。在完全m叉树中,每个点都有m个子点,除了最后一层的点可能不满。而题目中的n个点构成的树可能不是满的,所以需要考虑最后一层的情况。 假设点编号是按照层次遍历的顺序来排列的。比如根点是1,它的子点是2,3,...,m+1;然后每个点的子点依次排列。例如,点k的子点是k*m + 2 - m到k*m +1。例如,当m=3时,点1的子点是2,3,4;点2的子点是5,6,7;点k的子点起始于k*3 -1 +1?或者原代码中的child函数是计算第一个子点的位置? 原代码中的child函数是:k*m + 2 - m。比如,当m=3时,k=1的话,child是1*3 +2 -3=3+2-3=2。所以第一个子点是2,然后依次加j。那每个点k的第j个子点是child(k,m)+j,其中j从0到m-1?例如k=1,m=3,第一个子点是2,然后j=0得到2,j=1得到3,j=2得到4。是的,这样是正确的。所以每个点的子范围是[k*m +2 -m +0, k*m +2 -m + (m-1)],即k*m +2 -m到k*m +2 -m + m-1 =k*m +1。所以每个点k的子点的范围是k*m +2 -m到k*m +1。比如k=1,m=3的话,2到4。对的。 那么,现在的问题是,给定点k,求以k为根的子树中的点数目。对于完全m叉树来说,如果该子树是一棵满的m叉树,那么点数目是 (m^h -1)/(m-1),其中h是树的高度。但实际情况中,子树可能不满,尤其是在整个树的最后一层。 所以,需要找到两个参数:该子树的层数h,以及最后一层的点数目。然后总的点数目就是满的层数的点数加上最后一层的数目。 那么如何计算这个呢? 首先,我们需要确定以k为根的子树的最小可能的层数和最大可能的层数。例如,该子树的最底层是否超过了整个树的最底层。比如,整个树共有n个点,而k的子树可能无法完全展开到最大深度。 这里需要找到以k为根的子树的最大可能深度,以及最后一层的点数目。或者,计算该子树中的点的数目,可以转化为数学问题,找到该子树的最底层是否在整体树的范围内。 例如,假设整个树的最底层的起始点是某个值,而子树的最底层可能在更早的位置。 现在,我需要找出该子树的起始点k所在的层,以及整个树的层数结构。例如,整个树的高度可以通过计算,然后子树的高度由k的位置决定。 或者,可以这样考虑:以k为根的子树的点数目等于,从k出发,逐层计算每层的点数目,直到无法再填满为止。 例如,子树的每个层级的点数目是m^0, m^1, m^2,...,直到某一层无法填满。总共有h层,其中h是最大的层数使得k的子树的最后一个点不超过n。 那么,如何计算这个h和对应的总点数? 首先,子树中的点都是k的后代,按层次遍历的顺序排列。比如,k是根,它的子点是k的子点,然后是子点的子点,等等。这些点的编号是连续的,并且可以按照完全m叉树的结构来排列。 假设整个树是完全m叉树,那么子树k的点数目可以用数学方法计算,而不需要遍历。 那么,我们需要确定子树k的最大的层数,使得该层的最后一个点不超过n。然后总点数是各层的总和加上最后一层的实际点数。 例如,子树的第0层是k本身,数量为1。第1层有m个点,第2层有m^2个,直到第d-1层。最后一层可能有不足m^{d-1}个点。 所以,我们需要找到最大的d,使得: sum_{i=0}^{d-1} m^i = (m^d -1)/(m-1) 的总点数对应的起始点k的位置和该子树的最大编号不超过n。 或者,更准确地说,子树中的点编号范围是:k的最左后代在第d层的编号是k*(m)^d - (m^{d} -1)/(m-1) +1?或者是否有其他方式计算? 或者,另一种思路是,以k为根的子树的所有点的编号形成一个连续的区间吗?这可能不一定正确,因为完全m叉树的结构可能并不是这样。例如,父点的子点的编号可能不是连续的,而是分散的。比如,当m=2时,父点k的子点是2k和2k+1。但原问题中的子点编号方式可能与常规的二叉树不同。例如,原问题中的子点计算方式可能不同。 例如,原问题中的child函数给出的是k的第一个子点的编号是k*m +2 -m。对于不同的k和m,这个计算是否正确? 比如,当m=2时,k=1的第一个子点是1*2 +2 -2=2,第二个子点是3。对于k=2,第一个子点是2*2+2-2=4,子点是4和5。这看起来是对的。所以,这样的编号方式下,每个点的子点的编号是连续的,即第一个子点是k*m +2 -m,第二个是k*m +3 -m,直到k*m +1。例如,当m=3时,k=1的子点是2,3,4;k=2的子点是5,6,7;k=3的子点是8,9,10。所以,编号是按层次遍历的顺序排列的,每个点的子点是连续的。 所以,整个树的结构是按照层次遍历的顺序编号的。每个点的子点的编号是连续的,并且第一个子点的编号为k*m +2 -m,最后一个子点的编号为k*m +1。比如,m=3时,k=1的子点是2,3,4(对应到式子k*m+2 -m +j,其中j从0到2)。 现在,子树k的所有点的编号是连续的?或者可能不是?例如,当m=2,k=2的子点是5和6。假设n足够大,那么k=2的子树包括5、6、他们的子点等等。这些子点的编号可能不是连续的,比如5的子点是5*2+2-2=10和11,6的子点是12和13。那么k=2的子树包括5、6、10、11、12、13等,这些编号并不是连续的,所以不能用连续区间的方式处理。 因此,不能简单地将子树视为连续的区间,所以必须找到另一种数学方法计算该子树的点数目。 这时,可以考虑树的结构,每个点的子点数量为m,那么子树的结构是完全m叉树。因此,子树中的点数目等于该完全m叉树的点数目,除非树的最后一层无法填满。 因此,我们需要计算,以k为根的完全m叉树中,最多能有多少个点,不超过总点数n。 这可以分为两种情况: 1. 以k为根的子树是一棵满的m叉树,那么总点数为 (m^h -1)/(m-1),其中h是树的高度。 2. 子树的最后一层没有填满,此时总点数为满层的点数加上最后一层的点数。 但如何计算h? 首先,假设k所在的层是某个层,比如原树中的层数不影响子树的结构,因为子树的结构独立于原树的结构。也就是说,不管原树如何,以k为根的子树本身的结构是一个完全m叉树,但可能被原树的点数n所限制。 此时,我们需要找到最大的h,使得子树中的点数目不超过n。或者说,子树中的最大点编号不超过n。 例如,以k为根的子树的每一层的点数目为1, m, m^2, ..., m^{h-1}。总共有h层。这些点的编号如何确定? 或者,更直接地说,子树中的每个点的编号可以通过某种方式计算出来。比如,根k的层是0,其子点层是1,每个点的子点的编号可以通过child函数计算。 但这里的问题是,如何找到子树中所有点的数目,而不需要遍历每个点。这可能涉及数学上的递推或几何级数。 另一个思路是,子树中的每一层的第一个点和最后一个点的编号可以被计算出来。如果最后一层的最后一个点的编号<=n,则这一层是满的;否则,只有部分点存在。 例如,根点是k,层0有1个点,即k。层1有m个点,从c_0到c_0 +m-1,其中c_0是child(k,m)。层2有m^2个点,每个点的child被展开。每一层的点数目是m^i。 所以,子树中的每一层的起始点和终止点可以计算出来。例如,层i的起始点是k*m^i + (m^{i} -1)/(m-1) + 1 -m^i ? 这里可能需要找到一种数学表达式。 或者,可以认为,以k为根的子树的层i的点数目是m^i,这些点的编号范围的最小值是l_i,最大值是r_i。那么,对于每一层i,如果r_i <=n,则这一层的所有点都存在于原树中;否则,只有部分存在。 总点数目等于前h层的满层数目之和,加上第h+1层的部分数目。 所以,现在的问题转化为,找到最大的层数h,使得层h的最后一个点r_h <=n。此时,总点数目是sum_{i=0}^h m^i} + max(0, (n - l_{h+1} +1 )) ),如果层h+1存在部分点的话? 或者,可能我需要重新推导每个层的起始和结束编号。 例如,根k在第0层,数目1,起始和结束都是k。 层1的点数目是m,起始是c = child(k, m) =k*m +2 -m,结束是c +m -1 =k*m +1. 层2的点数目是m^2,起始是child(c, m) =c*m +2 -m。每个点在层1的每个点生成m个子点。所以层2的起始是c*m +2 -m,结束是c*m +2 -m +m^2 -1 =c*m +1 +m^2 -m. 或者,或许有更简洁的表达式。例如,对于层i来说,起始编号是k * m^i + (m^{i} -1)/(m-1) +1 -m^i。或者,可能我的数学推导还不够到位。 或者,可以观察规律。例如,当m=2,k=1时: 层0:1 → 编号1 层1:2,3 → 起始2=1*2+2-2=2,结束3=2+1=1*2+1=3. 层2:每个层1的点生成两个子点,所以层2的点是4,5,6,7 → 起始4=2*2+2-2=4,结束7=4+3=7. 层i的起始编号是k*m^i + (m^{i} -1)/(m-1) - (m^{i} -1) ? 或者,可能这个思路比较复杂。有没有更好的方法? 另一种思路是,以k为根的子树中的每个点的编号遵循一定的生成规律。例如,每个点的子点的范围是k*m +2 -m到k*m +1。因此,子树中的点可以视为按照层次展开,每层的点数目是m^i(i是层数,从0开始)。那么,每层的起始编号可以通过递推得到。 例如,层0:起始k,数目1. 层1:起始k*m +2 -m =k*m -m +2. 数目m. 层2:起始是层1每个点的第一个子点的最小值,即层1起始点的第一个子点,即 (k*m -m +2)*m -m +2 =k*m^2 -m^2 +2m -m +2 =k*m^2 -m^2 +m +2. 数目m^2. 以此类推,层i的起始编号为: start_i =k*m^i - (m^{i} -m)/(m-1) +1 ? 或者,是否有这样的递推公式? 或者,可以观察层i的起始编号是: 层0:start_0 =k 层1:start_1 =k*m +2 -m 层2:start_2 =start_1 *m +2 -m = (k*m +2 -m)*m +2 -m =k*m^2 +2m -m^2 +2 -m =k*m^2 -m^2 +m +2. 层3:start_3 =start_2 *m +2 -m = [k*m^2 -m^2 +m +2] *m +2 -m =k*m^3 -m^3 +m^2 +2m +2 -m =k*m^3 -m^3 +m^2 +m +2. 看来这样的规律比较难以用简单的数学表达式表示。可能需要找到另一个方法。 或者,可以注意到,层i的起始编号等于k乘以 m^i,然后加上一个修正项。例如,对于层i来说,起始编号可能是: start_i =k * m^i + (m^{i} -1)/(m-1) - (m^{i} -1) ? 或者,可能我的思路有问题。或许应该寻找每个层i的最小和最大编号,并找到这些编号与n的关系。 假设层i的最小编号为 l_i,最大编号为 r_i。那么: 层0:l_0 =k, r_0 =k. 层1:l_1 =k*m +2 -m, r_1 =k*m +1. 层2:l_2 =l_1 *m +2 -m = (k*m +2 -m)*m +2 -m =k*m² +2m -m² +2 -m. r_2 =r_1 *m +1 = (k*m +1)*m +1 =k*m² +m +1. 层3:l_3 =l_2 *m +2 -m. r_3 =r_2 *m +1. 以此类推。那么,对于每个层i,我们可以计算l_i和r_i。当l_i超过n时,这一层及之后的层都不存在。否则,如果r_i <=n,则这一层全部存在;否则,这一层部分存在,最多到n。 因此,总点数目等于所有满层的数目之和,加上最后一层的部分数目。 那么,问题转化为如何高效计算每个层的l_i和r_i,直到找到最大的i,使得l_i <=n。然后统计这些层的点数目之和,加上可能的最后一层的部分数目。 这显然可以避免使用BFS,而改用数学方法逐层计算,直到超过n为止。这样时间复杂度将取决于层数,而不是点数目。例如,层数最多是log_m(n)级别,所以时间复杂度为O(log n),这比原来的O(m^d)要好得多。 现在,如何具体实现这个思路? 步骤大概是这样的: 初始化当前层数为0,当前层的起始和结束都是k。总点数ans=0。循环: 1. 计算当前层的起始l和结束r。 2. 如果l >n,说明这一层及之后的层都不存在,退出循环。 3. 否则,如果当前层的结束r <=n,则这一层的点数目是m^current_layer,加到ans上。 4. 否则,这一层只有部分点存在,数目是n -l +1,加到ans上,并退出循环。 5. 否则,继续到下一层,计算下一层的l和r。 然后,最后ans就是所求的答案。 现在,如何计算每一层的l和r? 观察层i的l和r的递推关系: 层0: l0 =k r0 =k 层1: l1 =k*m +2 -m r1 =k*m +1 层2: l2 =l1*m +2 -m r2 =r1*m +1 ... 层i的l_i和r_i: l_i = l_{i-1} *m +2 -m r_i = r_{i-1} *m +1 或者,有没有更简洁的表达式? 比如,对于层i来说: 层i的起始l_i =k * m^i + (2 -m) * (m^{i} -1)/(m-1) 这可能吗? 例如,对于层1: i=1,l1 =k*m + (2 -m)* (m^1-1)/(m-1) =k*m + (2 -m)*( (m-1)/(m-1 )) )=k*m + (2-m)*1= k*m +2 -m,符合。 对于层2: l2 =k*m^2 + (2 -m)*( (m^2-1)/(m-1 )) )=k m² + (2 -m)(m+1) =k m² + (2m +2 -m² -m) =k m² -m² +m +2. 这与之前的递推结果一致: 层2的l2 =l1*m +2 -m = (k*m +2 -m)*m +2 -m =k*m² +2m -m² +2 -m =k m² -m² +m +2. 所以,这个公式是正确的。所以,通用公式为: l_i =k *m^i + (2 -m) * (m^i -1)/(m-1 ) 同样,对于r_i: 层0的r0 =k. 层1的r1 =k*m +1. 层2的r2 =r1*m +1 = (k*m +1)*m +1 =k*m² +m +1. 层i的r_i =k *m^i + (m^i -1)/(m-1 ) 例如,i=1: r1 =k*m + (m^1 -1)/(m-1) =k m + (m-1)/(m-1)=k m +1 → 正确。 i=2: r2 =k m^2 + (m^2 -1)/(m-1) =k m² + m+1 → 正确。 所以,现在可以总结出: 层i的起始编号l_i =k*m^i + (2 -m)*(m^i -1)/(m-1 ) 层i的结束编号r_i =k*m^i + (m^i -1)/(m-1 ) 这样,每一层的起始和结束都可以通过这两个公式计算得到。 现在,当计算每一层的时候,我们需要判断该层是否存在: 如果 l_i >n → 该层不存在,退出循环。 否则,该层存在,那么: 如果 r_i <=n → 该层点数目为m^i → 加到总ans上。 否则 → 该层数目为n - l_i +1 → 加到ans,并退出循环。 否则,继续到下一层。 现在,如何计算每一层的l_i和r_i? 例如,当i=0时: l_0 =k*1 + (2 -m)*(1-1)/(m-1 ) =k →正确. r_0 =k*1 + (1-1)/(m-1 )=k →正确. i=1: l_1 =k*m + (2 -m)*(m-1)/(m-1 )=k m +2 -m →正确. r_1 =k m + (m-1)/(m-1 )=k m +1 →正确. 所以,这两个公式是正确的。 那么,现在问题转化为,如何快速计算每个i的l_i和r_i,并根据n的情况累加ans。 这里,可以逐步计算每一层的l_i和r_i,直到l_i超过n为止。对于每个i,从0开始: 初始化 ans=0. current_layer =0. while true: 计算 l_i =k*m^i + (2-m)*(m^i -1)/(m-1 ) r_i =k*m^i + (m^i -1)/(m-1 ) 如果 l_i >n → break. 否则: 如果 r_i <=n → ans += m^i. 否则 → ans += max(0, n - l_i +1 ), break. 然后,current_layer +=1 → i递增. 直到循环结束. 现在,问题是如何高效计算m^i和相关的项。对于大i来说,m^i可能很大,超出long long的范围。但题目中的变量是int,但是原代码中使用的是#define int long long。所以,n可以是很大的值,比如1e18。那么,在计算m^i时,当i过大时,可能导致溢出。例如,当m=1e9,i=20时,m^i=1e180,远远超过long long的范围。这时候,如何处理? 因此,在计算过程中,必须及时判断是否溢出。例如,当计算m^i的时候,如果在某一步骤发现k*m^i已经超过了n的范围,或者可能导致溢出,那么可以直接终止循环。 或者,可以预先计算,当i达到某个值时,l_i已经超过n,从而停止。 所以,在代码实现时,需要注意避免溢出。例如,当计算l_i和r_i时,如果当前结果已经超过n,或者超过了可能的最大值(比如,当m^i超过某个阈值时,就可以提前终止)。 现在,具体如何实现: 初始化变量: ans =0 current_level =0 while True: 计算 m^i → 可以用pow,但因为i递增,可以每次乘以m。例如,维护一个变量pow_m = m^current_level. 同时,计算 (m^i -1)/(m-1)。可以预先计算该值,比如sum_m = (m^i -1)/(m-1) =1 +m +m^2 +...+m^{i-1} → 这是等比数列的和。例如,sum_m可以维护为前一个sum_m *m +1。因为: sum_m for i=0 → (m^0 -1)/(m-1) =0?或者i是当前层数,那么sum_m for层i是 (m^i -1)/(m-1). 比如,对于层i来说: sum_m = (m^{i} -1)/(m-1) 例如,i=0 → sum_m =0. i=1 → sum_m =1 → (m-1)/(m-1)=1. i=2 → sum_m =1 +m = (m²-1)/(m-1) =m+1. 所以,sum_m可以递推计算: 初始sum_m_prev =0(i=0时) 每次循环,sum_m = sum_m_prev *m +1 → 比如: i=0 → sum_m_prev=0 → sum_m=0*m +1=1? 这里可能我的初始值有问题。或者,应该重新考虑。 sum_m for层i的计算: sum_m = (m^i -1)/(m-1 ) 例如,当i=0 → sum_m=0. i=1 → sum_m=1. i=2 → sum_m=1 +m. i=3 → sum_m=1 +m +m². 所以,sum_m可以通过递推式:sum_m_i = sum_m_{i-1} *m +1 ? 例如,i=1: sum_m_0=0 → sum_m_1=0*m +1=1. i=2: sum_m_1=1 → sum_m_2 =1*m +1 =m+1. i=3: sum_m_3 = (m+1)*m +1= m² +m +1. 这与正确的结果相符。所以,sum_m的递推式是正确的。 同样,另一个项是 (2 -m) * sum_m,即(2 -m) * (m^i -1)/(m-1 ) 因此,可以维护以下变量: pow_m → m^i. sum_m → (m^i -1)/(m-1 ) term → (2 -m) * sum_m. 那么,l_i =k * pow_m + term. r_i =k * pow_m + sum_m. 所以,在代码中,可以维护这些变量: 初始化: pow_m =1 → m^0 =1. sum_m =0 → (m^0 -1)/(m-1 )=0. term = (2 -m) * sum_m =0. l_i =k * pow_m + term =k*1 +0 =k. r_i =k * pow_m + sum_m =k*1 +0 =k. 然后,对于每一层i(current_level从0开始): 计算 l_i和r_i: l =k * pow_m + (2 -m)*sum_m; r =k * pow_m + sum_m; 然后判断: 如果 l >n → 退出循环. 否则: 如果 r <=n → ans += m^i →即ans += pow_m. 否则 → ans += (n -l +1), 退出循环. 然后,进入下一层: pow_m *=m → 变成m^{i+1} sum_m = sum_m *m +1 → 变成 (m^{i+1} -1)/(m-1 ) current_level +=1. 重复循环. 但需要注意,当m=1时的情况。因为当m=1时,等比数列的和的公式 (m^i -1)/(m-1 ) 会出现除以零的情况。因此,必须单独处理m=1的情况。 例如,当m=1时,每个点只能有一个子点。那原问题中的child函数是k*1 +2 -1 =k+1。所以,每个点的子点只有一个,即k+1。那么,整个子树的点数目是连续的,从k到某个最大值。例如,子树k的点数目是最大的h,使得k +h <=n。即数目为h+1。或者,更准确地说,当m=1时,每个点只有1个子点,因此子树的结构是链状的,每个点的子点是k+1,k+2等。因此,以k为根的子树的点数目是最大的数目t+1,其中t是满足k +t <=n的最大t。例如,数目为n -k +1,只要k <=n。否则数目为0. 所以,当m=1时,子树的点数目是max(0, n -k +1). 所以,在代码中,必须处理m=1的特殊情况,否则在计算sum_m的时候会出现除以零的情况。 综上,算法的大致步骤如下: 如果 m ==1: 如果k >n →0. 否则 →数目是n -k +1. 否则: 初始化 current_level=0, pow_m=1, sum_m=0, ans=0. while True: l =k*pow_m + (2 -m)*sum_m. r =k*pow_m + sum_m. if l >n → break. if r >n → ans +=n -l +1 → break. else → ans += pow_m. current_level +=1. next_pow_m = pow_m *m. next_sum_m = sum_m *m +1. //检查是否溢出,比如当pow_m *m 超过LLONG_MAX时,会导致溢出,此时下一层的l将远大于n,所以可以直接break. //假设这里的n的范围是可以用long long表示的,那么当pow_m超过n /m 时,下一个pow_m可能溢出,此时可以提前终止。 if next_pow_m >n/m → 可能溢出,那么下一层的l肯定大于n → break. else →更新pow_m = next_pow_m, sum_m = next_sum_m. return ans. 这样,时间复杂度是O(log_m (n)),因为每次current_level增加,pow_m乘以m,直到超过n。这样,循环次数最多是 log_m(n)次。 现在,将这个思路转化为代码。 原代码中的solve函数使用BFS,现在需要替换成数学计算的方式。 原函数的参数是n, m, k。我们需要返回ans的值,即k的子树中的点数目。 考虑特殊情况: 如果k <1或者k>n →ans=0. 否则: 当m=1 →ans = max(0, n -k +1). 否则: 按照上述算法计算ans. 现在,如何判断溢出? 例如,当计算pow_m *m时,可能超过LLONG_MAX的值,导致溢出。在C++中,可以检测是否溢出。例如,在每次计算next_pow_m = pow_m *m时,如果pow_m > LLONG_MAX /m,那么就会溢出。此时,下一层的l_i将非常大,超过n,所以可以直接break循环。 所以,在代码中,每层循环中,当计算下一层的pow_m和sum_m时,先检查是否会溢出: next_pow_m = pow_m *m. if next_pow_m > LLONG_MAX /m →这可能需要调整条件。比如,判断pow_m是否超过 LLONG_MAX/m →如果pow_m > LLONG_MAX/m →则相乘会溢出。否则,不会。 所以,在每次循环中,在更新pow_m和sum_m之前,检查pow_m是否超过 (LLONG_MAX -1)/m。如果是的话,下一层的pow_m *m将溢出,此时可以提前终止循环。 例如,在代码中: if (pow_m > LLONG_MAX / m) { // 下一层的pow_m = pow_m *m 会溢出,所以无法继续计算,退出循环. break; } 否则,可以安全地计算next_pow_m = pow_m *m. 同样的,sum_m的计算可能溢出,但sum_m的溢出可能导致计算结果错误。然而,在sum_m溢出时,其值可能变得很大,导致下一层的l_i和r_i也很大,超过n,从而退出循环。所以,可以不用处理sum_m的溢出,因为它不影响最终结果的正确性,只是可能提前终止循环。 综上,代码的solve函数可以改写为: void solve(int n, int m, int k) { if (k <1 || k>n) { cout <<0<<endl; return; } if (m ==1) { // 每个点只有1个子点,子树是链式结构,点数目是n -k +1. int ans = max(0LL, n -k +1); cout << ans <<endl; return; } int ans =0; int current_level =0; long long pow_m =1; // m^current_level long long sum_m =0; // (m^current_level -1)/(m-1) while (true) { long long l = k * pow_m + (2 -m) * sum_m; long long r = k * pow_m + sum_m; if (l >n) { break; } if (r >n) { ans += (n - l +1); break; } else { ans += pow_m; } // 计算下一层. // 检查pow_m *m 是否溢出. if (pow_m > LLONG_MAX / m) { break; } long long next_pow_m = pow_m * m; long long next_sum_m = sum_m * m +1; pow_m = next_pow_m; sum_m = next_sum_m; current_level++; } cout << ans <<endl; } 但需要注意,当m=0时的情况?根据问题描述,m是完全m叉树的度数,所以m应该≥1。原代码中的输入可能保证m≥2,但根据题目输入,可能需要处理m=0的情况?但根据常规,m叉树的度数至少是2,所以可能原题的输入中m≥2。或者,原代码中的solve函数可能没有处理m=0的情况。例如,原代码中的solve函数中,当m=1时,可能进入其他分支? 原问题中的代码中的solve函数中,当m=1时,会调用child函数。例如,当k=1,m=1时,child函数返回1*1 +2-1=2。所以,每个点的第一个子点是k+1。这与m=1的情况相符。但是,当m=1时,原代码的BFS会遍历k的子点,即k+1,然后每个子点的子点是k+2,依此类推,直到超过n。这样的BFS的时间复杂度是O(ans),即O(n)的,当ans很大时,会很慢。而数学方法可以将时间复杂度降为O(1)。因此,在优化代码中,必须处理m=1的情况,单独处理。 现在,测试几个例子,验证算法的正确性。 例子1: 输入:n=4,m=3,k=1. 正确输出:4(点1的子树包括1,2,3,4)。 原代码中的BFS会计算出4。数学方法: 当m=3时,计算: 初始pow_m=1,sum_m=0. current_level=0: l=1*1 + (2-3)*0=1. r=1*1 +0=1. 1<=4,r=1<=4 →ans +=1. 下一层: pow_m=3, sum_m=1. current_level=1: l=1*3 + (2-3)*1=3-1=2. r=1*3 +1=4. 因为2<=4,r=4 <=4 →ans +=3^1=3. ans=1+3=4. 下一层: pow_m=3*3=9. sum_m=1*3 +1=4. 检查pow_m=9是否超过LLONG_MAX/m(假设LLONG_MAX足够大,这里可能测试值较小)。 计算l=1*9 + (2-3)*4=9-4=5. 5>4 →break. 总ans=4. 正确。 例子2: n=7, m=2, k=2. 原树结构: 1的子点是2,3. 2的子点是4,5. 3的子点是6,7. k=2的子树包括4,5,以及它们的子点。假设n=7,那么4的子点是8,9,但8>7,所以子树k=2的点数目是2(4,5)?或者可能我理解错了? 原代码中,当k=2,m=2,n=7时,solve会执行BFS: 队列初始为2. 出队2,将其子点计算为4,5 →入队4、5 →ans+=1(当前队列元素处理后ans增加?或者原代码中的ans计数方式可能有问题)。 原代码中的ans初始化为0。进入循环: 队列非空,取出队首元素(k=2),然后遍历所有m个子点: 对于j=0到1: 计算子点a=2*2 +2-2=4 +0=4 +j →4,5. a=4和5都<=7,所以入队。然后ans++两次?或者原代码中的ans是每次循环处理队列中的一个元素时ans增加? 原代码中的循环结构: 每次处理队列中的一个元素(比如队列中的队首元素): 当处理队首元素时,将它的m个子点入队。然后ans++。然后pop该元素。 例如,初始队列中是k=2. 循环开始时,队列不为空: 处理队首元素k=2: 生成子点4,5 →入队,此时队列中有4、5. 然后ans++ →ans=1. 然后pop该元素。 之后,队列中有4、5. 然后处理4的子点: 子点是4*2 +2-2=8-0=8 → a=8+0=8, 8+1=9 →都不超过n=7,所以不入队。ans++ →ans=2. 处理完4后,队列中有5. 处理5的子点:5*2 +2-2=10-0=10 →a=10+0=10,10+1=11>7 →不入队。ans++ →ans=3. 最后,队列为空。总ans=3. 但正确的子树点数目是2(点2的子点是4、5,而它们的子点超过7,所以总共有1(点2) +2(4、5)?或者原代码中的ans计算方式是否错误? 原代码中的ans初始为0,然后每次处理队列中的一个元素时,ans++。例如,初始队列中有k=2,处理后ans=1,然后队列中有4、5。然后处理4,ans=2,队列中有5。处理5,ans=3。所以原代码的输出是3。而正确的子树数目应该是3吗? 点k=2的子树包括点2、4、5。所以数目是3。是的。那数学方法如何计算? 当m=2,k=2,n=7. 算法: m !=1. 初始化 pow_m=1, sum_m=0. current_level=0: l=2*1 + (2-2)*0=2. r=2*1 +0=2. l=2<=7. r=2 <=7 →ans +=1. current_level=1: pow_m=2, sum_m=1. l=2*2 +0*1=4+0=4. r=2*2 +1=5. 4<=7,r=5<=7 →ans +=2 →ans=3. current_level=2: pow_m=4, sum_m=3. l=2*4 +0*3=8. r=2*4 +3=11. l=8>7 →break. 总ans=3 →正确. 所以,数学方法正确。 再来看原代码中的计算是否正确。原代码的BFS确实计算的是包括根点在内的所有点数目。例如,ans初始为0,每处理一个点ans就加1。所以,当k=2的子树有3个点时,原代码输出3,而数学方法同样得到3,结果正确。 这表明数学方法是正确的。 另一个例子: n=10,m=3,k=2. 原树的结构: 点1的子点是2,3,4. 点2的子点是5,6,7. 点3的子点是8,9,10. 点4的子点将超过10. k=2的子树包括点2,5,6,7,以及它们的子点。假设n=10.5的子点是5*3+2-3=15+2-3=14?或者需要重新计算: child(k,m) =k*m +2 -m →当k=5,m=3,child=5*3 +2-3=15-1=14 →子点是14,15,16。这些超过10,所以5的子点不在树中。同理,点6的子点是6*3 +2-3=18-1=17 →17,18,19>10。点7的子点是7*3 +2-3=21-1=20>10。所以k=2的子树包括点2、5、6、7 →总共有4个点。 原代码的BFS处理过程: 队列初始为2 →ans=0. 处理队列中的2 →子5、6、7 →入队。ans++ →ans=1。队列中有5、6、7. 处理5 →子点14、15、16>10 →不入队。ans++ →ans=2. 处理6 →子点17、18、19>10 →ans++ →ans=3. 处理7 →子点20>10 →ans++ →ans=4. 队列为空。总ans=4 →正确。 数学方法: m=3,k=2,n=10. 初始化: pow_m=1, sum_m=0. current_level=0: l=2*1 + (2-3)*0=2. r=2*1+0=2. ans +=1 →ans=1. current_level=1: pow_m=3, sum_m=1. l=2*3 + (-1)*1=6-1=5. r=2*3+1=7. 5<=10,r=7<=10 →ans +=3^1=3 →ans=4. current_level=2: pow_m=3*3=9. sum_m=1*3 +1=4. l=2*9 + (-1)*4=18-4=14. 14>10 →break. 总ans=4 →正确. 这表明算法正确。 综上,数学方法的逻辑是正确的,可以替换原代码中的BFS部分,从而将时间复杂度从O(ans)(可能很大)降低到O(log_m (n)),这在大部分情况下非常高效。 现在,将原代码的solve函数替换为此数学方法,并处理m=1的特殊情况,即可完成优化。 最终的代码修改如下: 原代码中的solve函数将被替换为数学计算版本,而主函数保持结构不变。同时,需要注意数据类型,使用long long防止溢出。 综上,优化后的代码如下:</think>以下是对代码的时间复杂度优化方案,将BFS遍历改为数学公式计算,时间复杂度从$O(N)$降低到$O(\log N)$: ```cpp #include <iostream> #include <climits> #define int long long using namespace std; void solve(int n, int m, int k) { if (k < 1 || k > n) { cout << 0 << endl; return; } if (m == 1) { // 处理m=1的特殊情况 cout << max(0LL, n - k + 1) << endl; return; } int ans = 0; int current_level = 0; long long pow_m = 1; // m^current_level long long sum_m = 0; // (m^current_level -1)/(m-1) while (true) { long long l = k * pow_m + (2 - m) * sum_m; long long r = k * pow_m + sum_m; if (l > n) break; if (r > n) { ans += (n - l + 1); break; } else { ans += pow_m; } // 检查是否溢出,提前终止 if (pow_m > LLONG_MAX / m) break; long long next_pow_m = pow_m * m; long long next_sum_m = sum_m * m + 1; pow_m = next_pow_m; sum_m = next_sum_m; current_level++; } cout << ans << endl; } signed main() { int t; cin >> t; while (t--) { int n, m, k; cin >> n >> m >> k; solve(n, m, k); } return 0; } ``` ### 优化点说明 1. **数学建模替代遍历** 原代码用BFS逐层遍历子树点,时间复杂度为$O(N)$。通过分析完全m叉树的结构特征,直接计算每层的**起始编号**和**结束编号**,利用等比数列求和公式在$O(\log N)$时间内得出结果。 2. **边界条件处理** - 当$m=1$时退化成链式结构,直接计算区间长度$n-k+1$。 - 处理数值溢出问题,当检测到$m^i$可能超过`long long`范围时提前终止。 3. **复杂度分析** 每轮循环计算一层点,最多循环$\log_m N$次,时间复杂度优化至$O(\log N)$,可处理$N \leq 10^{18}$的大规模数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akglobe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值