记NOIP2016

本文记录了一位参赛者在NOIP竞赛两天的经历及心得。第一天因未能顺利解决部分题目而感到沮丧,第二天调整心态,发挥相对稳定。文章详细描述了每道题目的解题思路与反思,最终取得了468分的成绩。

Day0

早上坐大巴到福州,车上放音乐听得完全睡不着。吃完午饭后听sgg神犇说要打题感觉完全方了,然而还是在房间看了一中午手机。下午看考场mywkll发现了调试的迷之bug,话说程序直接放E盘下不能调试是什么鬼畜设定啊hhhh。晚上吃完饭后回酒店依旧在浪,群里开了视频听神犇们欢唱到十一点多,感觉有点睡不着。

Day1

临考前一点紧张感都没有,感觉不像在比赛。
看到题目t1依旧是签到题,被图中的mengbier逗笑了,思考了很久mogician是什么单词,考完后经神犇提醒才反应过来。看了看t2刚开始以为是树剖+标记。然后看t3,诶这不是个普通的背包吗,又看了数据范围,n,m<=2000,貌似Onm完全没有问题啊。
然后开始敲代码。t1快速水过。敲t2的时候写完树剖就懵逼了,yy了一下发现不知道怎么搞,想了想大概可以写个lca+dfs序+主席树乱搞?于是把前面的注释掉写了个倍增和dfs,本来打算记一下深度等于wi并且起点在 i 的子树里的个数,然后想了想从起点到lca和从lca到终点还要分开写以及lca到根的部分貌似要去掉……好麻烦,感觉noip应该不会考主席树(?),于是又想了很久没想出什么好的处理方法(是我太蠢了),看了看时间已经十点多了先去打t3。一开始记了第 i 节课在哪个教室结果写完dp样例1就挂了,思考了一下多加了一个状态,改成有没有申请以及有没有成功,然后样例1对了样例2又挂了,改了几个智障bug结果答案还是不对orz,看到数据那么大一点调试的心情都没有。此时是上午十一点,想了想又回去码t2,结果死活调不出来。十一点半的时候感觉自己大概是没希望了于是打算码暴力,想想说不定t3只是手贱改完就对了(……)于是又回去看t3结果没看出来,然后只打了t2的40分暴力而且来不及检查。
最后期望得分第一题100第二题40第三题0(再见)。
出考场的时候心情复杂,一边下楼梯一边思考人生。其实今年本来以为noip应该没多大问题所以一直不怎么在意,结果第一天就爆炸。跟机房神犇吐槽今年题目画风不对,完了之后突然觉得没什么意思,说到底还是我太菜了。意外的是当带队老师问我情况的时候我已经能很平静(?)地说出“t2t3都挂了”,——然后老师脸都黑了。在交谈中知道t3暴力可以76分然而我居然没打一心想调正解,更重要的是一个傻逼dp我居然写错了?!然后t2虽然代码量大但是也可以蹭60~80,话说今天真的不是暴力大赛吗?
最后还是自己给自己喂了碗鸡汤(?),反正去年都有noip300-的省队呢,我不虚,我不虚,我不虚!
嗯,祝明天的自己加油。

Day2

今天的题目貌似比较友好。t1照样水。t2看完题目以为写个堆就可以了,看了看数据范围7*10^6好像会挂,然后就yy了一下m<=3*10^5用堆以及q=0的情况根据单调性写三个队列。t3看到数据范围n<=18,这不就是个状压吗?
开始敲代码。t1应该没有问题。t2敲完了以上两种情况然后又写了个对拍,看数据范围好像这样可以水过90,然后思考正解,感觉应该是类似q=0的线性做法,然后yy了很久感觉q>0没有什么单调性(到底为什么不举个例子试一下?!),于是去码t3,写了个状压dp感觉大概没有什么问题。开始出数据测试。试了一下t2的m=3*10^5,跑了1500+ms,想了想决定写个读入优化,1400+ms,然后又yy了一下输出优化,1100+ms。【再见】。因为之前没有写过输出优化怕会翻车于是又删掉了,反正最多被卡一个点,说不定评测机跑得比较快呢hhhh。算了一下第三题发现两重循环的状压dp复杂度好像爆炸,发了一会儿呆突然想到两个点确定一条抛物线所以最多On22n,不知道极限数据卡不卡得过去不过懒得再想优化了,又打开程序看了一眼文件名,刚想关掉突然瞄到一行

const int eps=1e-8;

我:……
闷声改double。
然后又回去想t2到比赛结束也没yy出来。
最后期望得分第一题100第二题85~90第三题85~100。
出考场后听到大家都说t1t2很简单,老师听说我t2没写出正解脸又黑了。拿出手机刷了刷QQ,看到某个群里说t2写三个队列维护就可以了,然后紧接着下面claris说对。
我:这不是q=0的解法吗?q>0怎么会单调?
过了一会儿sgg突然说好像是对的,举了个例子试了一下,诶诶诶我考试的时候在想什么?!就这么错过了正解QAQ。然后又看到有人问t3会不会卡精度,突然感觉好虚。

于是今年的noip大概是要400分收场了(说不定更低),比去年菜了不是一点点啊,事实证明我果然是来打酱油的。貌似今年暴力分有500+,不知道分数线要高到哪里去了,现在只希望成绩出来的时候自己能平静地接受,然后好好努力准备省选(如果还有机会的话)。

最后得分

于是我来打自己的脸了。

两天的t1就不说了。
D1t2蹭分打错最后只有25暴力分。
D1t3迷之88,话说我明明连dp方程都是错的居然还能得这么高分啊哈哈哈哈(连傻逼dp都写错了我到底在得意什么)。
D2t2意料之中的90。
D2t3炸成65,这代码能力也是没谁了,我大概需要回炉重造一波。

最后得分468,看来还能混个省一,接下来的省选得加油了。

<think>嗯,用户问的是NOIP2016提高组第三题“海港”的解题思路和算法分析。首先,我得回忆一下这个题目的具体要求。根据引用里的信息,题目大概是关于处理到达海港的船只,每艘船有乘客的国籍数据,需要实时统计最近24小时内的不同国籍数量。用户可能想要一个高效的算法,因为直接遍历所有乘客肯定会超时。 首先,题目中的时间窗口是24小时,也就是86400秒。每次新船到达时,需要移除所有超过24小时的旧数据,然后统计剩下的国籍数。直接每次检查所有录显然不行,时间复杂度会很高,特别是当数据量大时。 那如何处理时间窗口呢?滑动窗口或者队列结构可能有用。因为每次新船到达时,只需要移除时间超过当前时间-86400的录,这样可以用队列来维护乘客录,保证队列里的时间都在窗口内。这样可以避免每次全量检查,只需从队头开始移除过期的。 然后统计国籍数量。这里的问题是如何高效地维护国籍的出现次数。每添加一个乘客,对应国籍的计数加1;当移除过期乘客时,对应国籍的计数减1,当减到0时,总的国家数减1。这样需要维护一个哈希表或字典来录每个国籍的数量。 但乘客是按船到达的,而每艘船可能有多个乘客。所以可能需要将每个乘客的信息分开存储,或者按批次处理。比如,队列中的每个元素是一批乘客(同一艘船的),并录他们的到达时间和国籍列表。当处理新船时,先检查队列头部的船是否超出时间窗口,如果是,就依次将这些乘客的国籍计数减少,直到所有过期的船都被移除。 这样,每次处理新船时,需要先处理队列中过期的船,然后添加当前船的乘客,并更新国家计数。这样时间复杂度应该是线性的,因为每个乘客只会被加入和移除一次队列。 可能的难点是,如何处理同一时间多艘船的情况,或者时间跨度较大的情况。比如,如果有很多船都在时间窗口内,那么队列中的元素可能很多,但每次处理时只需要移除超过时间窗口的部分,而不是全部。 举个例子,假设当前时间是ti,那么队列中保存的是所有到达时间大于ti-86400的船。当新船到达时间ti时,我们需要将队列中所有时间<=ti-86400的船移除,并更新国籍计数。然后,将当前船的乘客加入队列,并更新计数。这样就能保证队列里的数据都在窗口内,统计国家数的时候只需查看当前哈希表中计数大于0的国家数量。 这样,主要的数据结构是队列,每个元素包含时间和该船的所有乘客国籍,以及一个哈希表录国籍的出现次数,还有一个变量录当前的国家数量。 另外,需要考虑时间ti的范围。题目中ti可能很大,但每次只需要比较相对时间差是否超过86400,所以不需要担心时间溢出问题。 可能的优化点:队列中的每个元素可以存储该船的时间和国籍列表,这样处理过期船时,逐个出队,并减少对应国籍的计数。当国籍计数减到0时,总的国家数减1。 现在,总结一下步骤: 1. 初始化一个队列,用来保存每艘船的到达时间和乘客国籍列表。 2. 维护一个哈希表(字典)录每个国籍的出现次数。 3. 维护一个变量count录当前的有效国家数。 4. 对于每艘新到达的船: a. 检查队列头部的船是否超过当前时间ti-86400。如果是,依次出队,并减少对应国籍的计数。如果某个国籍的计数减到0,则count减1。 b. 将当前船的乘客国籍加入哈希表,如果该国籍之前计数为0,则count加1。 c. 将当前船的信息入队。 d. 输出当前的count值。 这样,每个乘客的国籍被处理两次(入队和出队),时间复杂度是O(K),其中K是所有乘客的总数,应该可以通过题目中的时间限制。 需要注意的点: - 处理时间时,要确保比较的是船的时间是否在窗口内。例如,当前船的时间是ti,那么窗口的有效范围是ti-86400+1到ti?或者ti-86400到ti?需要看题目具体要求,比如,是否包括刚好24小时前的船。比如,题目中的描述可能是“在24小时内的”,也就是ti - t <= 86400,或者严格小于? 根据引用3中的样例输入,比如第一艘船时间1,第二艘船时间4,第三艘时间10。假设样例输出是3、4、4。这说明当第三艘船时间为10时,前两艘船是否在窗口内取决于窗口是否包含10-86400到10。但样例中的时间可能较小,所以需要明确比较条件是 ti - prev_t >=86400吗? 比如,假设当前处理的时间是ti,那么队列中保留的是那些船的时间 > ti -86400。当处理新的ti时,将所有队列中时间<=ti -86400的船移除。 例如,ti=86400,那么移除所有时间<=0的船。ti=86401,则移除时间<=1的船。这样,每个船在队列中的时间必须严格大于ti-86400,否则会被移除。 所以在代码中,当处理新船时,先循环检查队列头部的船的时间是否<=当前ti -86400,如果是,就出队,并减少该船所有乘客的国籍计数,如果国籍计数减到0,则总count减1。然后处理当前船,将乘客国籍加入哈希表,若加入前计数为0,则count加1,然后将该船的信息入队。 这样,每个乘客被处理两次,时间复杂度是线性的。 所以,这样的算法应该可以满足题目的要求,避免超时。 现在,用户可能还需要具体的代码实现,或者更详细的步骤解释。比如,如何处理队列中的每个船的数据结构,如何录每个船的乘客国籍列表,以及如何维护哈希表和count变量。 例如,在Python中,可以用一个deque作为队列,每个元素是(时间,国籍列表)。哈希表可以用字典。然后遍历每个新船,先处理过期数据,再处理当前数据。 可能的代码结构: from collections import deque q = deque() nationality_count = {} current_count = 0 n = int(input()) for _ in range(n): ti, ki, *nationalities = map(int, input().split()) # 处理过期船只 while q and q[0][0] <= ti - 86400: old_t, old_nations = q.popleft() for nation in old_nations: nationality_count[nation] -= 1 if nationality_count[nation] == 0: current_count -= 1 # 处理当前船的乘客 new_nations = nationalities[:ki] for nation in new_nations: if nationality_count.get(nation, 0) == 0: current_count += 1 nationality_count[nation] = nationality_count.get(nation, 0) + 1 q.append( (ti, new_nations) ) print(current_count) 不过,这里需要注意,当处理旧的船只时,可能需要遍历该船的所有国籍,并逐个减少计数。例如,假设一个船有多个相同国籍的乘客,那么每个乘客都需要处理,不能合并,因为每个乘客的国籍都会影响计数。 但是,如果同一艘船上有多个相同国籍的乘客,那么在出队时,每个都要减少一次计数。比如,如果一艘船有3个国籍为2的乘客,那么在移除该船时,国籍2的计数减3。当减到0时,current_count减1。 但原题中的输入中,每艘船的ki个乘客可能有重复的国籍。例如,引用3中的样例输入: 3 1 4 4 1 2 2 2 2 2 3 10 1 3 其中第二艘船有ki=2,两个乘客国籍都是3。 那么在处理这艘船时,加入哈希表中国籍3的计数是加2。当该船被移除时,国籍3的计数减2,如果原来计数是2,减后变为0,则current_count减1。 所以,在队列中的每个元素需要保存该船的所有乘客国籍的列表,包括重复的国籍。例如,第一艘船的国籍列表是[4,1,2,2],这样出队时,每个国籍都要处理一次。这样,如果同一艘船中有多个相同国籍的乘客,在出队时会逐个减少计数,可能导致多次减到0的情况,但实际上只有当最后一个减少到0时才需要调整current_count。 比如,假设国籍2的当前计数是3,在出队时减去2次,变成1,那么current_count不需要变化。但如果减到0的话,就需要减少current_count。 所以,代码中需要确保每个乘客的国籍都被处理,这样哈希表的计数正确,而current_count的增减只在计数从0到1或1到0的时候触发。 所以,上述代码中的处理是正确的,因为当处理旧船时,遍历该船的所有国籍,逐个减少计数,如果某个nation的计数减到0,则current_count减1。 但是在加入新船时,可能多个相同国籍的乘客被加入,此时,只有当该国籍的计数从0变为1时,current_count才加1。其他情况下(比如原本已经有该国籍的计数),current_count不变。 这样,current_count始终维护的是当前不同国籍的数量。 这个逻辑是正确的。 综上,解题思路是使用队列维护时间窗口内的船只,哈希表维护国籍的出现次数,并在处理每艘新船时,先移除过期的船,再添加当前船的乘客国籍,最后输出当前的国籍数量。</think>### NOIP2016提高组第三题'海港'解题思路 #### 问题分析 题目要求统计每艘船到达后,过去24小时内所有乘客的不同国籍数量[^3]。核心挑战在于高效处理时间窗口内的数据,避免暴力遍历导致超时。 #### 算法设计 1. **滑动窗口**:使用队列维护时间窗口内的船只信息。每次新船到达时,移除所有超过24小时的旧数据。 2. **哈希表计数**:录每个国籍的乘客数量。当某国籍的计数从0变为1时,总国家数加1;当计数减到0时,总国家数减1。 #### 具体步骤 1. **初始化队列和哈希表**: - 队列存储船只的到达时间和乘客国籍列表。 - 哈希表`nationality_count`录国籍出现次数。 - 变量`current_count`表示当前有效国家数。 2. **处理每艘船**: - **移除过期数据**:检查队列头部船只的时间是否超过当前时间$t_i - 86400$,若过期则出队,并减少对应国籍计数。若某国籍计数归零,则`current_count`减1。 - **添加新数据**:将当前船的乘客国籍加入哈希表。若国籍计数从0变为1,则`current_count`加1。 - **输出结果**:每处理完一艘船,输出`current_count`。 #### 代码示例 ```python from collections import deque q = deque() nationality_count = {} current_count = 0 n = int(input()) for _ in range(n): data = list(map(int, input().split())) ti, ki = data[0], data[1] nations = data[2: 2 + ki] # 移除过期船只 while q and q[0][0] <= ti - 86400: old_t, old_nations = q.popleft() for nation in old_nations: nationality_count[nation] -= 1 if nationality_count[nation] == 0: current_count -= 1 # 处理当前船只 for nation in nations: if nationality_count.get(nation, 0) == 0: current_count += 1 nationality_count[nation] = nationality_count.get(nation, 0) + 1 q.append((ti, nations)) print(current_count) ``` #### 复杂度分析 - 时间复杂度:$O(K)$,其中$K$为总乘客数,每个乘客仅被加入和移除队列一次。 - 空间复杂度:$O(K)$,队列和哈希表存储所有乘客信息。 #### 优化点 1. **合并重复国籍**:若同一艘船的乘客国籍重复,可合并计数减少操作次数。 2. **延迟删除**:在极端时间跨度下,队列可能存储大量过期数据,但实际操作中队列长度受窗口限制,无需额外优化[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值