已更新到2018.9.23:第六场比赛
题目大意: 两个数列,每个数列中有 n n n个数,要从中取数,取一个数有 w ( > 0 ) w(>0) w(>0)的代价,收益为在 A A A中取的数的和,与在 B B B中取的数的和,这两个和的最小值。求收益-代价的最大值。
我的思路: 原题的数据范围中给出了 A , B A,B A,B已经排好序的性质,实际上如果不给这个性质的话你手动排序就行,就是不容易看出来。
可以肯定的是,最优解中 A , B A,B A,B中取的数一定都是对应最大的。那么我们先枚举哪一边是最小值,然后在两边用两个指针进行计算,因为收益是最小值,所以另一边只要达到枚举的最小值就不再继续了,因此是最优的,于是我们就 O ( n ) O(n) O(n)解决了这一题。
这道题我在场上就拿到了满分。
题目大意: 给定长为 n ( ≤ 50000 ) n(\le 50000) n(≤50000)的一个序列 A A A,可以进行区间翻转操作,翻转区间 [ l , r ] [l,r] [l,r]的代价是 r − l + 1 r-l+1 r−l+1。构造出一种操作方案使得代价不超过 2 × 1 0 7 2\times 10^7 2×107,并使得操作后序列 A A A中的数字按照从小到大的顺序排列。
我的思路: 我在场上写了 O ( n 2 ) O(n^2) O(n2)的暴力拿了65分,然后在考试结束前半个小时想到了(疑似)正解,可惜码力实在不够写不出来。
正解的思路大概是从序列中只有 0 0 0或 1 1 1的特殊性质入手。考虑分治,每次的意图是把 1 1 1凑在区间的右边。先递归处理左半和右半区间,然后处理当前区间时,只需要一次翻转把左半区间的 1 1 1凑到右边即可。显然这样的代价不超过 n log n n\log n nlogn,所以直接模拟翻转操作,时间复杂度就是 O ( log n ) O(\log n) O(logn)。
当序列是任意序列时,可以借用快速排序的思想,选定一个中间点,然后把比它小的放前面,否则放后面。选中间点就是 O ( n log n ) O(n\log n) O(nlogn)的了,而这个放置的过程,实际上我们只要给比中间点小的数标 0 0 0,否则标 1 1 1,就和上面的特殊性质一模一样了。于是外层用快速排序的分治,对于每个区间的排列也分治,这样代价就不超过 n log 2 n n\log^2n nlog2n,时间复杂度也同样了。
题目大意: 给定一棵生成树的边权集合,求有多少点数为 n ( ≤ 40 ) n(\le 40) n(≤40)的,边权在 [ 1 , n ( n − 1 ) 2 ] [1,\frac{n(n-1)}{2}] [1,2n(n−1)]中的,不同边边权不同的无向完全图,满足存在边权集合为给定集合的最小生成树。
我的思路: 这题我在考场上只想出了边权集合为 1 1 1到 n − 1 n-1 n−1的方法…
我们知道Cayley定理:无向完全图的带标号生成树数目是 n n − 2 n^{n-2} nn−2。而当边权集合为 1 1 1到 n − 1 n-1 n−1时,显然我们只需要枚举生成树的位置,其他的边无论怎么填都不会影响最小生成树的位置。于是答案就是 n n − 2 ⋅ ( n − 1 ) ! ⋅ ( n ( n − 1 ) 2 − n + 1 ) ! n^{n-2}\cdot (n-1)!\cdot (\frac{n(n-1)}{2}-n+1)! nn−2⋅(n−1)!⋅(2n(n−1)−n+1)!。
本来可以拿到40分的,结果傻逼了忘记取模,爆成10分…
其实我考场上有一个思路和正解的思路非常接近,但很遗憾没有时间细想下去。按Kruskal的思想从小到大填边,在填一条树边的时候,一定要是连接原来分开的两个连通块,反之,则一定是在原来的某个连通块中连。注意到这个过程仅和所有点的连通块的状态有关,并且并不用求出真的连通状态,只需要表达出各大小的连通块的数量就行了。这样的状态数有多少个呢?注意到这就是整数拆分数 P n P_n Pn,打表可知 P 40 P_{40} P40大约为 30000 30000 30000多。接着就可以按照上面的方法转移了,不过好像还要优化,因为我还没细想,所以就先到这里吧。
第一天我的分数是100+65+10=175,有诸多遗憾,甚至一度认为自己连NOIP都考不出来了…不过这些题目也是很好地开阔了我的思维,受教了。
题目大意: 定义一个字符串的回文拆分为,将其拆分成 k k k段,使得第 1 1 1和第 k k k段,第 2 2 2和第 k − 1 k-1 k−1段…都相等。对每个字符串,求最大的 k k k。
我的思路: 由于考试策略的原因,这道题我是在最后写的…
我们需要一个结论:在进行两端的分割的时候,一定是割出最短的合法相等子串是最优的。这个可以用反证法自己证明。然后我们就可以用字符串哈希做到 O ( n ) O(n) O(n)了。
然而考场上我非常傻逼地写了一个KMP只拿了70…太毒了…
题目大意: 有一个长为 n ( ≤ 50 ) n(\le 50) n(≤50)的只包含 A B C D ABCD ABCD四个字母的字符串,每次操作可以交换两个相邻字符,也可以在给定的 m ( ≤ 100 ) m(\le 100) m(≤100)个操作中选择一个,这些操作的形式都是把某一个特定的子串换成长度相等的另一个子串,也是只包含 A B C D ABCD ABCD四个字母。求一个操作序列,使得途中经过的不同字符串的数量最多,输出这个数量。
我的思路: 首先,由于可以交换字符,所以当字符的数量相等时,不同的字符串可以相互转换,因此我们就可以用 ( a , b , c , d ) (a,b,c,d) (a,b,c,d)这样的状态表示有 a a a个 A A A, b b b个 B B B…的字符串集合。而因为字符串长度 n n n总是不变,因此可以省掉一维。
接下来,因为可以交换字符,所以只要字符数达到要求,就一定可以交换出一个可以进行操作的子串,用这样的要求去判断和连边即可。
连完边之后,我们发现可能有环,因此进行SCC缩点,然后对得到的有向图做拓扑序DP,找到带权最长路即可。
要注意的是,答案可能超过long long的范围,但不会超过两个long long拼起来的范围,因此要使用一些玄学操作来进行计算。反正我不太会…所以在考场上只得到了80分。
题目大意: 有一棵有 2 n ( n ≤ 20 ) 2^n(n\le 20) 2n(n≤20)个叶子节点的完全二叉树,叶子一开始从左到右编号为 1 1 1到 2 n 2^n 2n,非叶子节点从上到下分层,第 i i i层从左到右编号为 2 i − 1 2^{i-1} 2i−1到 2 i − 1 2^i-1 2i−1。现在有 m m m次操作,一个是给定一个区间 [ l , r ] [l,r] [l,r],依次交换点 l l l到点 r r r的左右子树。另一个是询问现在从左到右第 x x x个叶子节点的编号。要注意的是,叶子节点的编号是随着旋转而变化的,而非叶子节点的编号单纯按位置来定。
我的思路: 正解用线段树,暂时不是很会,只写了60分的部分分。
首先30分可以暴力模拟…就不说了。然后我做的是特殊性质,注意到如果把点的编号化成二进制串,那么题中的树就是一棵trie,而对于区间 [ 2 x , 2 y − 1 ] [2^x,2^y-1] [2x,2y−1]进行操作,实际上就是把第 x + 1 x+1 x+1到第 y y y层的 01 01 01索引交换了,因此在找的时候如果这一层的索引被交换了奇数次,就往反方向走就行了。
这次比赛考得就不错了,70+80+60=210,得到了第三名,虽然也有点遗憾(第一题傻逼了),但还是考出了应有的水平(?)吧。
题目大意: 将 1 1 1到 n ( ≤ 1000 ) × m ( ≤ 1000 ) n(\le 1000)\times m(\le 1000) n(≤1000)×m(≤1000)的排列填在一个 n × m n\times m n×m的方阵中, q ( ≤ 5 × 1 0 5 ) q(\le 5\times 10^5) q(≤5×105)个询问,每次询问一对 ( x , y ) (x,y) (x,y),表示询问有多少个数在当前行是第 x x x大,在当前列是第 y y y大。
我的思路: 这题挺水的…把 n × m n\times m n×m到 1 1 1按顺序填入方阵,一个数在填入的时候,在当前行中已填入的数字的数目,就是当前行比它大的数字的数目,因此很自然地得到排名,列也是同理,就这样记录一下即可 O ( 1 ) O(1) O(1)回答每个询问,时间复杂度 O ( n m ) O(nm) O(nm)。
题目大意: 用 m ( ≤ 5000 ) m(\le 5000) m(≤5000)种颜色填入有 n ( ≤ 5000 ) n(\le 5000) n(≤5000)个小格的纸条,求对于任意连续 m m m个格子里,必然存在两个格子颜色相同的填色方案数。
我的思路: 这题我是最后做的,在考场上想了两个小时…然而还是凉了…
赛后同校AFO大佬说:“这题挺简单的啊…”并一下就说出了怎么转移,仔细一想还真的很有道理…
令 f ( i , j ) f(i,j) f(i,j)为前 i i i个格子中填色,其中从后往前最长一段不包含相同颜色格子的后缀长度为 j j j的方案数。那么在转移时,如果不选择最后 j j j个格子中的颜色,那么最长后缀长度就变成 j + 1 j+1 j+1,有 m − j m-j m−j种颜色可供转移。如果选择最后 j j j个格子中的颜色,那么最长后缀长度就会变成 1 , 2 , . . . , j 1,2,...,j 1,2,...,j,各有 1 1 1种颜色转移。于是得到状态转移方程:
f ( i , j ) = ( m − j + 1 ) f ( i − 1 , j − 1 ) + ∑ k ≥ j f ( i − 1 , k ) f(i,j)=(m-j+1)f(i-1,j-1)+\sum_{k\ge j}f(i-1,k) f(i,j)=(m−j+1)f(i−1,j−1)+∑k≥jf(i−1,k)
转移的时候用后缀和优化一下就可以达到 O ( n m ) O(nm) O(nm)的复杂度了。而此时,要计算最长后缀长度达到过 m m m的方案数比较麻烦,不如补集转化一下,反过来求最长后缀长度不曾达到过 m m m的方案数更加简单,只需要不考虑 f ( i , m ) f(i,m) f(i,m)的影响即可,最后用 m n m^n mn减去这个方案数就是答案了。
题目大意: 一个游戏有 n ( ≤ 1 0 5 ) n(\le 10^5) n(≤105)个角色排成一个序列,有 m ( ≤ 10 ) + 1 m(\le 10)+1 m(≤10)+1个等级( 0 , 1 , . . . , m 0,1,...,m 0,1,...,m),有 m m m个参数 a 1 , . . . , a m a_1,...,a_m a1,...,am,满足角色经验值 ≥ a x \ge a_x ≥ax的最大 x x x就是角色的等级,要求维护区间加经验值(一定非负),单点修改经验值(不一定增加),和区间求等级和。
我的思路: 这个题还是比较好想的。对每个角色,维护他的上一级等级(角色当前等级 + 1 +1 +1)的 a a a值减去他的经验值。显然,当这个值变成非正数的时候,就表示这个角色升级了。因为每个角色最多升级 m m m次,所以我们在线段树中维护区间最小值,如果非正就暴力向下修改即可。还有一个单点修改,其实也一样,一次修改后最多升级 m m m次,因此总的升级次数还是 O ( n m ) O(nm) O(nm)的级别,于是时间复杂度就是 O ( m n log n ) O(mn\log n) O(mnlogn)。
这道题思路这么通畅,结果我爆成20分…凉了…
这些题目都不难,然而我却崩成了100+20+20=140分这样惨不忍睹的结果,同校若干刚学OI的都比我高…可能我真的要NOIP退役了吧…不过这场比赛因为网站被DDOS攻击,unrated了,好吧…
题目大意: 有一个带边权的无向图,要求删掉一些边,使得图满足:对于任意一个点集,点集中连的边的数量严格小于点集中点的数量。求删掉的边的边权和的最小值。
我的思路: 容易证明,满足要求的图只可能是森林,因为一旦存在环,那么环本身就不满足题目的条件。而要是删掉的边权和最小,那自然要求的是留下的边权和最大,也就是求一个最大生成森林,直接Kruskal就可以了。
题目大意: 有 n ( ≤ 5 × 1 0 5 ) n(\le 5\times 10^5) n(≤5×105)个可重集,元素都在 5 × 1 0 5 5\times 10^5 5×105范围内,所有集合的元素个数总和也在 5 × 1 0 5 5\times 10^5 5×105范围内。每次选择一个集合,然后集合中将会随机删掉一个数,当删掉的数中有重复值时结束,求在最优策略下,最坏的情况需要选择几次集合才能结束,如果删完后还不存在重复值也不算结束。
我的思路: 神仙题,暂时还看不懂题解,讲一下搜索的思路吧…
首先如果只有一个集合,那答案显然就是集合中不同的数的数目 + 1 +1 +1,当然,首先这个集合内必须存在两个数字相同。于是我们就能喜提 10 10 10分的好成绩了。
接下来,我们可以把这个过程看做某种博弈,我们付出代价选择区间,对方来删数,我们想让总的选择次数最小,对方恰恰相反。于是我们就可以搜索了,喜提另外 20 20 20分。
正解好像需要证明一个结论:最优策略下,无论何种情况都只会涉及其中两个集合。但是我并不会证,虽然场上也有一点这样的感觉,但就算有这个结论我还是不知道该怎么做,所以就先这样吧…
题目大意: 给定一棵带边权的树,路径的长度定义为路径上边权的异或和,再给定 m ( ≤ 3 × 1 0 5 ) m(\le 3\times 10^5) m(≤3×105)条额外边,有 q ( ≤ 3 × 1 0 5 ) q(\le 3\times 10^5) q(≤3×105)个询问,每次询问当区间在 [ l , r ] [l,r] [l,r]内的额外边存在时,从某点 S S S到某点 T T T的最小路径长度是多少。
我的思路: 这题非常显然要用到WC某题的结论:路径长度只有可能是树上的路径异或上一些基本环。这结论我就不再证了。而每当存在一条额外边,就表示存在了一个基本环,那么要找到 S S S到 T T T的树上路径,异或上一个区间内某些数能得到的最小值,一眼就想到线段树维护线性基,于是…
我们就炸了,因为合并线性基的复杂度为 O ( 3 1 2 ) O(31^2) O(312),所以上述算法的复杂度为 O ( 3 1 2 m log m ) O(31^2m\log m) O(312mlogm),只能通过 50 50 50分的数据。场上我就只想到这一步了。
而正解特别傻逼,我不知道为什么我没想到:直接分治,每次处理过中点 m i d mid mid的区间的询问。我们处理出所有 [ i , m i d ] [i,mid] [i,mid]和 [ m i d + 1 , i ] [mid+1,i] [mid+1,i]这样的区间的线性基,因为插入一个数进线性基的复杂度为 O ( 31 ) O(31) O(31),所以这一部分的复杂度为 O ( 31 l e n ) O(31len) O(31len)( l e n len len表示当前区间长度)。而处理询问就特别简单了,一个询问被中点切成两部分,直接把两部分的线性基合并即可,因此处理每个询问的复杂度是 O ( 3 1 2 ) O(31^2) O(312)的。这样的话,分治的复杂度为 O ( 31 m log m ) O(31m\log m) O(31mlogm),处理询问的总复杂度为 O ( 3 1 2 q ) O(31^2q) O(312q),因此比上面的做法少一个 log \log log,可以通过此题。
这次比赛还是非常遗憾,没有体现出应有的水平,不过100+30+50=180分的成绩居然也让我排在了第8名,难道大佬都去学文化了…?
题目大意: 一个长为 n + 1 n+1 n+1的序列,元素都是 1 1 1~ n n n中的整数,且每个整数都至少会出现一遍,对每个 k ( k = 1 , 2 , . . . , n + 1 ) k(k=1,2,...,n+1) k(k=1,2,...,n+1),求这个序列中长为 k k k的本质不同的子序列数目。
我的思路: 挺好想的一道小组合数学题。首先把两个相同元素的位置找到,然后开始算长为 k k k的本质不同子序列数:首先,如果不选或全选这两个元素,那么每一种选择的组合都是本质不同的,这一部分答案就是 C n − 2 k + C n − 2 k − 2 C_{n-2}^k+C_{n-2}^{k-2} Cn−2k+Cn−2k−2。主要考虑两个相同元素中只选一个的情况,先不管算重的问题,直接求的话答案是 2 C n − 2 k − 1 2C_{n-2}^{k-1} 2Cn−2k−1,但会有算重的部分,而算重的部分就是,除了所选的那个元素外,选择的所有元素要么同时在两个相同元素的左边,要么同时在右边,于是令左边元素左边的元素数为 x x x,右边元素右边的元素数为 y y y,算重的部分就是 C x + y k − 1 C_{x+y}^{k-1} Cx+yk−1,减去一个这个就是答案了。所以答案为: C n − 2 k + C n − 2 k − 2 + 2 C n − 2 k − 1 − C x + y k − 1 C_{n-2}^k+C_{n-2}^{k-2}+2C_{n-2}^{k-1}-C_{x+y}^{k-1} Cn−2k+Cn−2k−2+2Cn−2k−1−Cx+yk−1,预处理阶乘和阶乘逆元就可以 O ( 1 ) O(1) O(1)计算每个 k k k的答案了。
题目大意: 一个长为 n n n的序列 A A A,要删去其中恰好 k k k个数,使得剩下的数中, A i = i A_i=i Ai=i(权值和下标相等)的元素数量最多,求这个最多的数量。
我的思路: 一道比较难想的思维题。首先肯定想到求出每个数距离它该到的位置有多远,这个数就是 i − A i i-A_i i−Ai。我们可以把这看做,一个元素要达到目标位置,它前面必须恰好删掉 i − A i i-A_i i−Ai个数。于是我们要做的是,从序列中选择一个最长的满足这样条件的子序列:子序列中相邻两个元素 A i , A j A_i,A_j Ai,Aj,它们之间的数的数量( j − i − 1 j-i-1 j−i−1)大于等于 ( j − A j ) − ( i − A i ) (j-A_j)-(i-A_i) (j−Aj)−(i−Ai),这样才能保证有足够的数删。特殊地,子序列中第一个数的 A i A_i Ai,必须满足它前面没取的数的数量大于等于 i − A i i-A_i i−Ai,其实也就相当于在数列前面增加了一个限制。同理,在数列后面也要加上一个限制,然后就是一个纯的偏序LIS问题了,因为上面的限制可以看做是三个限制条件: i < j , A i < A j , i − A i ≤ j − A j i<j,A_i<A_j,i-A_i\le j-A_j i<j,Ai<Aj,i−Ai≤j−Aj。三维偏序显然可以用CDQ分治做,考场上我写了这个方法拿到了80分。
然而,最大数据中 n = 1000000 n=1000000 n=1000000,这就必须要求常数很小的 O ( n log n ) O(n\log n) O(nlogn)或者 O ( n ) O(n) O(n)才行。其实我们发现, A i < A j A_i<A_j Ai<Aj和 i − A i ≤ j − A j i-A_i\le j-A_j i−Ai≤j−Aj两个条件,因为不等号方向相同,可以对项相加,把 ≤ \le ≤换成 < < <,就变成: i < j i<j i<j。这不就是第一个条件吗?所以我们发现 i < j i<j i<j这个条件完全是冗余的,于是就少了一个限制条件,成了二维偏序问题,于是将其中一维排序,用树状数组搞搞就好了,时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
题目大意: 给定一棵树,对树上的一个连通块定义价值为,连通块所包含的点集中,最长的一段连续编号的长度(看不懂就去看原题吧)。求所有包含点数小于等于 k k k的连通块的最大价值。
我的思路: 这道题我一看,这不就先二分个答案,然后像SDOI寻宝游戏那题一样,用set维护动态虚树大小就结了吗?于是乐滋滋地写完提交一气呵成,然后…被卡成暴力分。
实际上因为在编号区间左端点相同的情况下,包含的区间越长,连通块大小就越大,显然是单调的,所以用双指针代替二分答案就行了, O ( n log n ) O(n\log n) O(nlogn)稳过…
这次考试我100+80+40=220,险些掉出前10(某同学说我这次掉出前10他吃翔,看来没有如愿),发挥还算正常,非常遗憾的是第三题我满足于 O ( n log 2 n ) O(n\log^2n) O(nlog2n)的算法,而并没有去想更加优的做法,也没有生成极限数据对拍,从而导致了卡成暴力的悲惨结局,NOIP上绝对不能发生这样的事情。明天就是该系列比赛的最后一场了,加油吧。
题目大意: n × n n\times n n×n的棋盘上,有 m m m个皇后,皇后可以向八个方向攻击(水平竖直对角线),当然也可以从八个方向被击中,要求对所有 i = 0 , 1 , 2 , . . . , 8 i=0,1,2,...,8 i=0,1,2,...,8,求 t i t_i ti,表示被从 i i i个方向击中的皇后数目。
我的思路: 模拟大水题…对每行每列和每条对角线存个桶,其中对角线可以通过两坐标之和或之差来判定,并对每个桶求出最两端的皇后是哪两个,统计时直接看它在这个方向上,首先看是不是只有一个,如果是就没有贡献,否则看是不是两端,如果是就产生 1 1 1的贡献,否则产生 2 2 2的贡献,直接计数即可。
题目大意: 4 4 4个并排排列的梯子,有 n n n层,每层中 4 4 4个梯子中有且仅有一个有横木,现要求 4 4 4个梯子中存在一个梯子满足,每 h h h层中至少有一块横木(即横木之间高度差不能达到 h h h),求方案数。
我的思路: 高维DP题。考虑从下往上放横木,到第 i i i层,其中四个梯子最上层的横木到这一层的高度差分别为 d i s 1 , d i s 2 , d i s 3 , d i s 4 dis1,dis2,dis3,dis4 dis1,dis2,dis3,dis4,那么我们显然可以定义一个五维的状态,然后枚举在哪个位置放横木就可以转移了。注意如果高度差达到 h h h,就说明这个梯子已经不满足条件了,这时候我们就用 d i s = h dis=h dis=h这个标记表示它已经不满足条件了,即使再在这个梯子上放横木,它也是不满足条件,这时候 d i s dis dis就保持在 h h h不会再变了,转移的时候注意一下就好。
然而 O ( n h 4 ) O(nh^4) O(nh4)的复杂度在考场上只能拿到 70 70 70分。仔细思考的话,因为每一层都会放横木,所以一定有一维 d i s dis dis是零(或是 h h h),这样进行一些简化状态之后,状态数就是 O ( n h 3 ) O(nh^3) O(nh3)的了,然而具体我也还没弄清楚,就先这样吧。
题目大意: 给定一个序列,每个元素有一种颜色,区间询问,每次询问一个区间内有多少种颜色恰好出现 T T T次,所有询问中 T T T相同。
我的思路: 如果 T = 1 T=1 T=1,我们都知道离线处理+线段树要怎么做,现在变成了 T > 1 T>1 T>1,其实在右端点相同时,使得某种颜色恰好出现 T T T次的区间的左端点是一段区间,用链表可以维护出这段区间,右端点右移一位就是修改某种颜色的可行区间,那么在询问时,只要看这个左端点处于多少种颜色的可行区间中即可,区间修改单点询问显然可以差分后用树状数组解决,常数极小,时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
(然而莫队好像都得到了 90 90 90~ 100 100 100分的成绩…毫无思维复杂度的算法居然没被卡,可能这题本身也难卡莫队吧)
NOIP前最后一次参加这系列的模拟赛,这次也是我在分数上考得最好的一次:100+70+100=270分。不过因为这次题目确实比较套路和简单,光AK爷就有4个,所以我也就只得到了第7名。这一系列比赛参加下来,发挥一度失常,终于在最后一次比赛考出了自己能达到的水平,也是能为这个系列画上一个句号了吧。今后也要继续学习、刷题,为最终的目标进发。