NOIP2016 提高组题解

本文提供NOIP2016提高组D1T2天天爱跑步的三种解法,包括暴力算法、树链剖分和线性算法,并详细解释了线性算法的思路。此外,还分享了D2T3愤怒的小鸟的解题报告,探讨了预处理和抛物线过点问题。文章总结了作者在比赛中的经验教训,提醒注意精度和状态转移策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

。。蒟蒻被老师撵去写题解,无奈。没什么功夫去写两天的t1了,这里先放D1T2和D2T3吧


D1T2 天天爱跑步解题报告

感言:这道题当时在考场上修修补补,一点一点挤牙膏地得到了正解,可是竟然最后被下标坑了,蒟蒻就是借口多:-(

一种暴力算法O(n^2)

  • 考虑到最朴素的思想,对每个人一步一步的跑到起点终点的lca,并统计到当前点x的时间Ti,对答案统计

    预处理lca(nlogn的倍增算法)
    每个跑步者单独处理,不断累计Ti,即if(Ti==W[a])ans[a]++;
    for(int i=1;i<=n;i++)printf(“%d\n”,ans[i]);
  • 一共m位跑步者,每人最多跑n步,那么这个算法的时间复杂度是O(nm)的,期望得分25

一种树链剖分O(nlogn),此处指向whj大神的blog

  • 时间复杂度O(mlogn),期望得分100(在洛谷上实测95 tle一个点,倍增的锅)

那么这已经是一种AC算法了,难道下面就没有了吗?

怎么可能?:)

第三种方法:O(n)

约定第i个人起终点的lca(s[i],t[i])为lca[i],点i深度为dep[i]

  • 考虑可能对点u有贡献的第i个跑步者,lca[i]肯定在u或者u上方,否则不经过u点,即lca[i]==u||lca[i]∉subtree[u]
  • 基于此前提,只有两种情况(有重合部分):①s[i]∈subtree[u] ②t[i]∈subtree[u]
    分类讨论能被u看见的点v的情况
    ① v为i人起点s[i] dep[s[i]]-w[u]==dep[u]
    ② v为i人终点t[i] dep[s[i]]+dep[t[i]]-2*dep[lca[i]]-(dep[t[i]]-dep[u])==w[u],即dep[s[i]]-2*dep[lca[i]]==w[u]-dep[u]

我们发现如果做个变换,即dep[u]+w[u]==dep[s[i]]

与 w[u]-dep[u]==dep[s[i]]-2*dep[lca[i]]的话,式子右边是不变的!于是我们考虑用一个桶cnt[2]来记录满足条件的点的数量即可

  • 考虑使用cnt[2][600600]存储起点/终点的dep[s[i]]/dep[s[i]]-2*dep[lca[i]]等于j的点的数量
  • 保存在数组cnt[0 or 1][j]中,那么当dfs中当前点到u的时候,ans[u]就可以从cnt数组中得到,特殊情况是:u为第i人的lca时可能导致s[i]与t[i]各对ans[u]贡献一次,所以式子如下
  • ans[u]=cnt[0][dep[u]+w[u]]+cnt[0][w[u]-dep[u]]-(重复部分容易处理);//两下标为上面两个等式的左式,需要注意的是dep[s[i]]-2*dep[lca[i]]有可能是负数,下标可以统一+300000处理成正数(本人栽在这里了)
  • 维护cnt数组的方式容易想到就是在s[i],t[i],lca[i]上打标记,若当前点为s[i]就计入cnt[0][dep[s[i]]],t[i]同理,lca[i]就把两个标记再退出去
  • 然而如果对于每颗子树建立一个cnt,再把cnt合并的话,复杂度是n^2的。于是发现搜到以v为根的子树时后,对v贡献的只有两个下标,那么在搜索点v前,传两个参数a,b记录一下原来的cnt两个值,dfs(v,a,b)时ans[v]-=a+b即可

以下洛谷ac代码,20点共1232ms,极限数据n=m=300000用时312ms

#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long
#define N 600060
inline void rd(ll& x){
    stati
### NOIP 2017 提高 初赛题目解析 #### Pascal 编程语言的淘汰时间 关于 Pascal 编程语言何时不再被支持的问题,在选项中给出的时间点为 2022 年[^1]。 #### 归并算法最坏情况下的比较次数 对于两个长度均为 n 的有序数 A 和 B 进行归并操作时,如果仅考虑元素之间的比较作为基本运算,则在最坏的情况下,至少需要执行 \(2n - 1\) 次比较才能完成整个过程[^2]。 ```python def merge_sorted_arrays(A, B): merged_array = [] i, j = 0, 0 while i < len(A) and j < len(B): if A[i] <= B[j]: merged_array.append(A[i]) i += 1 else: merged_array.append(B[j]) j += 1 # Append any remaining elements from either list merged_array.extend(A[i:]) merged_array.extend(B[j:]) return merged_array ``` 此代码展示了如何有效地将两个已排序列表合并成一个新的已排序列表,并且在这个过程中进行了必要的比较来决定哪个元素应该先加入到新的列表当中去。当处理到最后一个元素的时候,实际上已经完成了 \(2n-1\) 次比较(假设两部分都含有 n 个不同大小的数),这正好对应于上述提到的最佳下界理论值。 #### 硬币分堆与查找不合格品的方法 给定一数量为 n 枚相同外观但可能有一枚质量不同的硬币成的集合 A ,通过天平称重的方式找出那枚与众不同的假币: 1. 计算 k 值等于向下取整后的 \(\lfloor{n / 3}\rfloor\); 2. 把这些硬币分为三份 X、Y 和 Z,每一份分别有 k 枚; 3. 如果 X 和 Y 的质量不相等的话,那么继续下一步骤;否则跳转至第 5 步; 4. 对较轻的那一侧进一步分割重复以上步骤直到找到唯一可疑对象为止; 5. 当剩下不超过两枚硬币时直接对比即可判断哪一个是异常者;而只有一枚的情况则默认其就是那个特殊个体[^3]. 这种策略利用了三分法的思想,每次都将待测样本缩小三分之一左右的比例,从而提高了效率同时也简化了解决方案的设计难度.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值