前言 这里是蒟蒻TIMEpings,这是我第一次真正动手写题解 (虽然之前有想过很多次) ,有误之处请各位大佬多多指出(=・ω・=)!
当蒟蒻走投无路时,这种解法是骗分的好助手
先输入6个参数,提前把p算出来(这样可能会影响精度,不推荐),向worm里存每个蚯蚓,edT算出来也算是半个暴力优化,在print_answer()里面会体现。 时间复杂度的主要承担者之一doworm()需要循环做m次。
“模拟的大框架”
spc++; //第一行输出记步 sort(worm+1,worm+1+now_worm_cnt,cmp); //降序自动快排咯 int topworm = worm[1]; //刀砍出头虫 if(spc==t) //步输出周期已到 { printf("%d ",topworm); //出头虫上榜 spc=0; //读秒归零 } cut(topworm); //砍 grow(); //长 now_worm_cnt++; //蚯蚓数量+1 return;定义的新运算规则就只有这一行,为的是让sort函数进行降序排序。不太了解的大佬可以百度一下ovo。 spc是一个独立的周期记步器,每进行一次doworm()读一步,当读到t步的时候,按题目要求输出当前最长的蚯蚓,并归零记步器。(AC题解对这个概念进行了优化) 这个完全模拟会TLE的其中一个原因,就是因为每进行一步模拟都会进行一次STL的sort快排,导致当m大的时候,特别是还有q的存在(蚯蚓长长)时,没法在1s内完成(´;ω;`)1。
“时间复杂度的第二承担者”
if(q==0) return; //对于q=0的数据的特殊优化 for(int i=2;i<=now_worm_cnt;i++) { worm[i]+=q; //每条原来的蚯蚓长度+q }首先q=0的特殊数据既然给到我们,那么就可以尽可能的去利用它。少一遍循环多一点希望。 for循环的起点和终点要结合cut()函数一起看,因为刚砍半的蚯蚓是不会立刻长长的,所以就忽略这两条。
(回到doworm())每切一次,蚯蚓的数量应该++。
(对第二行输出的处理)
printf("\n"); //空行 sort(worm+1,worm+1+now_worm_cnt,cmp); //神秘人物来后的排序 for(int i=1;i<=edT;i++) printf("%d ",worm[i*t]); //输出第i*t位的虫空行这个坑千万不要掉进去了 最后一次砍蚯蚓的时候没有进行sort,于是此处再进行一次。 edT在这里发挥了作用,算个暴力优化,少一点循环也多一点希望。
思路1的时间复杂度能骗个30多分了(´・_・`)。
发现自己没AC之后,第一时间肯定是上网查题解啦! 搜到的各大佬的写法,很多都提到了队列(还是3个队列),基本思路也差不多相似,不过也发现了我想的数组写法。自己在琢磨透之后,就想着能不能把更好看的数组模拟写法再写的好理解一点
思考: 1.第一遍sort之后,所有的蚯蚓应该是降序排列的。 2.一个有序队列中所有蚯蚓的生长,不会影响它们的有序性。 3.如果把放在首位的蚯蚓出队,不会影响其它蚯蚓的有序性。 4.将出队的蚯蚓按照要求砍半后,分别放入另外两个队列,如果此时要求较长的半条永远放入队列2,较短的半条永远放在队列3,由于先放入某队列的半条(在不考虑生长的情况下)长度永远大于等于后放入的半条长度,故这两个队列同样也是降序的。 5.让思考4与思考2结合,之前放入的半条蚯蚓本来就比后面的长,还比后面的多长长几次,于是更不会影响有序性。 这样想通之后可以发现,如果用3个队列分别存"初始的蚯蚓,较长的半条蚯蚓,较短的半条蚯蚓",在初始的蚯蚓sort完一遍后,3个队列从头到尾都会一直是有序的,不需要sort多次。
暴力的grow()就是让每一次的当前的蚯蚓的长度(不包括刚砍半的两个半条)都+q来维护长度。 转念一想,按照优化sort的思考2,既然每次生长都不会影响排序,那我这么勤奋的扫这么多遍维护为了什么呢! 但是又发现,当我在输出蚯蚓长度的时候,一定是要长长之后的长度。 而且在用队列2,3之后,每一次放入的半条蚯蚓,它的长长情况都是不同的,先放入的半条蚯蚓总是比之后放入的蚯蚓多长几倍的q。那更不能完全不管长长了。 怎么办呢…
如果我们用rnd这个整数代表doworm()的循环次数,那么…(用鼠标移到被下划线标记的运算有惊喜!) 对于worm[],里面的第i条蚯蚓的长度就应该是 worm[i]+(rnd-1)*q 对于另外那两个数组cut1[],cut2[],从下标0开始存半条蚯蚓,那么第i条的蚯蚓长度就应该是 cut1[i]+(rnd-2-i)*q 数组模拟队列真好用!
原来的print_answer()函数被我改成了doline2(),感觉更符合作用233
这里将原来的spc记步器改成了rnd%t==0,概念都是每t步输出当前最长蚯蚓。
get_worm()函数包含了取三个队列的最长队首与出队两个操作,除了那个突破天际的if条件有点吓人但是要这么写 其它的可以参照我的优化思想看懂! tmp即表示当前最长的蚯蚓的长度
注释里表示运算的意义 这里是用if让cut1数组里永远存较长的半条,cut2数组永远存较短的半条
在m秒的处理后,蚯蚓总量会达到n+m条(每次砍半多一条) 此处再次调用get_worm()函数,继承了三个队列的当前队首值,rnd全局变量此时停留在m+1,发现意义刚好是"做了m遍砍半操作后(的查询)",利用get_worm()可以完美读出m遍操作后三个队列里的蚯蚓当前长度。 i%t==0替换掉了原来的edT,原因是此时我们无法得到完整的每一条蚯蚓的正确长度与排序,需要一步一步搜索来得到topworm的值。
优化过后的时间复杂度大概是O(n),已经可以完美解决题目数据了! loj上速度暂时排名14,还没写读入和输出优化! 如果对解法有什么问题,或者是蒟蒻我写题解的过程中出了写问题,或者是表扬我 ,别忘记在下方评论(`・ω・´)!
STL的sort利用的应该是快排,时间复杂度为O(n)=nlog2n,在还有一层for在外层的时候就已经是O(n2)了;更别说q有效的时候…无论如何,面对n+m<=7100000的数据显然是无法接受的… ↩︎