2020.02.13【NOIP提高组】模拟A 组 总结

emmm......

估分:\(100 + 20 + 30 = 150\)
考场:\(100 + 0 + 0 = 100\)

想的是打完\(T1\)后打\(T2T3\)暴力的,结果\(T1\)自己出的数据一直时超,搞死了。。。
最后根本没时间打剩下的了。。。

\(T1\)

这一题可以说打了整场比赛。。。
首先是看到\(n\)比较小,然后设法就觉得有什么巧妙的方法可做。
首先打了个暴力,然后猜结论。。。感觉好像可以分治?假的。。。
答案似乎只有一种情况,但很快就被推翻了。。。
\(emmm\),世界离我远去。。。
然后开始看看有什么规律。
发现好像暴力有好多优化剪枝可以使用。
首先显然可以发现,如果当前\(a[i]>a[i+1]\)&&\(a[i]-a[i+1]<cf[x-1]\)的话(\(cf[i]\)表示\(2^i\)\(x\)表示当前准备使用第\(i\)种操作),是绝对不可行的。
然后我们发现,\(a[i]\)想要转到\(i\)的位置上,是可以知道他需要处在那些区间,而如果\(abs(a[i]-i)\)转成二进制后前面的位置还是\(1\)的话不可行。
具体意思就是说:你做了第\(1\)种操作,然后\(a[cf[n]]=1\),那么它不可能回到\(1\)的位置上,所以不可行。
后来又加了个剪枝,发现没什么用,继续搞搞正。
发现一个神奇剪枝。我们可以发现,当前交换两个区间,如果其他区间还有需要交换的话那一定不可行了。
那我们就看看,对于之后整合成的\(cf[x]\)长度的区间,有几个是当前不可行的。如果大于两个,那肯定是不行的了。
然后在看看,如果只有一个的话,那就将那个区间当前所分成的两个小区间交换(因为我们判断时的区间是下一个操作的长度的区间)。
如果有两个的话,那就暴力枚举,然后看看时候包含这两个大区间(中的各自任意一个小区间)即可。可以证明正确性。
然后就这样飘飘然地过了︿( ̄︶ ̄)︿

\(T2\)

一直搞\(T1\)\(T2\)基本没怎么看,但是感觉好像是一种树上维护什么的。
不管了。。。
赛后看了下网上的题解,什么\(set\)\(虚树\)乱七八糟,但看了OJ里的题解以后,算是大概明白了怎么做了。
它就是找到树的欧拉序,在欧拉序中把有宝箱的提取出来,形成一段数,两两数之间的树上距离的和再加上左端点和右端点的树上距离就是答案了。
为什么?因为欧拉序可以保证如果两个数之间没有数的话,那么这两个数之间也就没有其它点有宝箱的。
我们可以用线段树来维护,每个节点维护当前区间拥有宝箱的点之间的树上距离,并记录一下区间最左边有的点和最右边有的点。
这样很容易就能维护了。

\(T3\)

这道题。。。开始想错了,然后再看的时候感觉\(n<=1000\)是肯定可以拿到手的。
然后再考虑\(m<=300\),发现似乎有方法可以(想到了神奇的分类背包(额,就是将同样的一种物品(有多件)打包分成\(1,2,4,8...\)这样子的)),好像可以拿到手。
原来是不可以的啊。。。对于\(n\)那么大,我们可以考虑矩阵乘法,那这样就是\(O(m^3logn)\)的,可以过30分。
正解是\(NTT\),由于乘法不方便,所以要转化成加法,每个数都变成\(p^x[i]\),我们要使\(x[i]\)不相同,就需要找到\(p\),可以暴力找\(O(m^2)\)(不会时超),但大佬有\(O(mlogm)\)的方法
\(wyt\)学长:https://blog.csdn.net/jokerwyt/article/details/80456871
然后可以发现他就类似于一个\(DP\)\(f[i+j] = sigma{f[i] * b[j]}\)\(b[j]\)表示%\(m-1=j\)出现的数的个数)
发现其实就是个多项式乘法,可以直接用\(NTT\)来做,\(O(mlogmlogn)\)
有一点要注意的是,我们乘完以后项数会翻一倍,对于多出来的我们只需要把他们转移到\(m-1\)以前的数即可,否则项数变大会时超,且难统计答案。

总结

还是对时间的把控度不够啊。。。
一开始估算可以拿到\(150\)的,但是由于代码实现能力,以及思考的时候考虑不全,导致大量时间耗去。。。
唉,还是加油吧,算法还要多补补才行。。。
有些东西学了以后还要学会去运用。

posted @ 2020-02-13 19:49  jz929  阅读(126)  评论(0编辑  收藏  举报