4.29模拟赛赛后总结
4.29模拟赛赛后总结
比赛历程
早上睡醒已经是AM7:20,洗漱过后来到机房说要换题,也就是说开始的时间和大家没有差别,大约AM7:45开始看题。
AM8:00 好像才看了一遍题,想着这次一定要先把暴力打完。
分析了一波T1,感觉暴力可过的分挺多的,好像暴力就有\(O(qm)\)的复杂度了,于是开始打暴力。
AM8:35打完调完了暴力,看了一眼T2,脑海中大概过了一下题目的意思,然后去了躺厕所。
去厕所的时候想到了T1好像可以进行一些优化,暴力的找所有路径太暴力了,应该想怎么能够一次统计多条路径。
想到这个图似乎是个拓扑图(这个时候没有注意到\(ai<bi\)) 所以按照度数拍一下序,然后从先的点开始跑,根据dep统计一个边产生的贡献,并且这个贡献可不是乱给的,如果两个点能够到达同一个点,那么这个dep就得在其中一个起点出发的路径上断掉,dep就不必再加深。
当然当时的思路没有这么顺畅,不过总归是梳理好了,在大概9:10的时候打出来了代码,然后开始我Linux下的第一次对拍。
结果很扯的事情出现了,虽然我想着dep加深的过程中是一个等差数列,能够\(O(n)\)的统计答案为\(n^2\)的路径,但是随机数据下竟然有时候还没有暴力快。。
感觉有点自闭,好像是白搞了一样,不过正式的数据这个优化应该会有优势。
然后开始看T2,思前想后,以为m<=3有搞头,但是并不会容斥掉这个东西。。结果在AM10:30的时候只有一个暴力可以搞。
AM:10:40 接受现实并开始思考T3,今天的暴力时间好在比较充足,T3这个十分的小暴力模拟起来还挺费劲(其实就两个dfs)
AM:11:00,打完调完暴力思考后边的分数,感觉这个序列的数据可以分块,大概思考了一下如何维护块的信息,感觉可行,于是就开始码了。
开始码的时间大概要AM:11:20,框架刚出来就已经是AM11:30了,不过老师说要延长30分钟,还好。
AM11:50码完发现好个锤子,手造数据过不去,细节有问题,自闭了。
虽然大家都说只有暴力分
赛后发现&思考
1 人均得分60+30+10. 前6都是,但是我是40+30+10,不知为何,我T1 TLE了两个点,找原因未果。
2 T3链的数据确实可以分块,赛后又调了好大会儿。一开始发现有一个地方算错了块的边界,"blo*(id-1)+1"写成了“(blo-1) *id+1”.. 后来发现还有边界没有特判,再后来发现change的地方对于单点的判断忘记了异或懒标记。。。改完最后一个地方之后终于过了。以为正解也能够树分块。但是发现询问是一个发散的而不是针对链的。自闭了。
3 老师说T3是线段树二分套路,虽然我第一反应是分块。然后因为我们明明练过这个东西却仍然不会,我们又多了个专题作业。 感觉确实是这样,怎么学的没有忘的快呢。大概还是理解不够深刻,还有一点就是练习不够多。
4 学过的套路不会->复习->订题订不完->新东西没搞会->又遇到了然后不会->补->。。。。
由于这样一个传递关系,基本上一个东西遇到两三次才能够学会,而有的东西因为只遇到过一次,可能就没机会会了。而且由于要干的事情太多了,感到并没有做什么时间就过去了。接下来要考虑理清楚事情的顺序,安排一个优先级,分个必要和非必要,慢慢提高自己的熟练度和效率吧。如果东张西望,或者草草了事,那我感觉能干的事情只会更少,而未来能干的事情也会越来越少。
技术总结
T1 正解是DP,转移其实类似dfs的过程,然后可以通过比较度数来考虑dp转移是推还是拉,以此来做到dp记录的不重不漏,而由于题目中给出的条件,\(a_i<b_i\),每次读入的点进行排序之后可以从小到大枚举,然后顺次利用度数小的dp值来更新自己,然后自己更新度数大的,这样可以做到不重不漏,而且这样的一层枚举可以证明复杂度是\(\sqrt{m}\) 的,这样最终的复杂度就是\(O(M\sqrt{m})\)的。
T2 正解暂时不会。
T3正解暂时不会。但是这里需要讨论一下这道题的部分分,即第2和第3个点,用分块做思路确实不难,但是细节上需要一段时间的调试,而线段树二分就不一样了,线段树二分模式比较固定,更新方式类似分块,但是查询采用二分,可以向左向右二分最大值或者说二分区间和,直到区间和为0。
其实线段树二分比一般的二分还要简单,毕竟线段树的结构比较固定,简单的跳儿子就可以了。其实线段树二分实现的复杂不在于二分,而在于区间信息的维护。
以sinao这道题为例,线段树二分的代码是这样的:
int find(int cnt,int l,int r,ll val){//寻找最左
if(l==r) return tr[cnt].sum<val?n+1:l;//如果说,最终没有割,返回n+1
psd(cnt,l,r);
if(tr[ls].mx>=val) return find(ls,l,mid,val);
else return find(rs,mid+1,r,val);
}
这个难吗?显然不难.比一般的二分要考虑的东西还要少。这道题麻烦的在于信息更新的顺序。T3链的部分分也是类似的。
难的都不是板子,而是思想的灵活应用。
还有一道例题是脑洞治疗仪
find的方式是不太一样的,利用一般形式的二分查找。
int find(int l,int r,int brain){
while(l<r){
int x=qhole(1,1,n,l,mid);
if(brain>x) l=mid+1,brain-=x;
else r=mid;
}
return l;
}
这个写法的一个变形是这样的
int find(int L,int R,int brain){
int l=L,r=R+1;
while(l+1<r){
int x=qhole(1,1,n,L,mid);
if(x<=brain) l=mid;
else r=mid;
}
return l;
}
二者是\(log_2^2n\)的一个二分查找,前者则减去之后到另一个区间查找剩余,这个查找剩余的写法在平衡树里边比较常见,而最近才学了平衡树,于是现在比较喜欢这样写,后者是从最开始的询问L直接找到值的位置,这个变形是题解里的写法,殊途同归。