浅薄的理解,仅供娱乐。
[CQOI2009]中位数
题意
给一个排列,求中位数为 (b) 的长度为奇数的连续子序列个数。((n <= 10^5))
题解
不要想的太复杂,发现只关心数的相对大小,所以可以抽象为 (-1, 0, 1),然后就是求子序列和为 (0) 的序列个数,这个直接向左扫一遍,向右扫一遍然后用map记一下就可以了。
[SHOI2001]小狗散步
题解
因为题意太难抽象,所以就直接写题解了,一道网络流建模好题,关键是容易让人想到最短路等等,其实看看数据范围确实发现可以是二分图匹配,把所有距离小于关键点的点连边,跑二分图匹配就可以了,因为dinic解决二分图匹配的复杂度是 (O(msqrt n)),所以数据范围可能出的不会很小,需要特殊考虑。
[NOI2001] 食物链
题意
A 吃 B,B 吃 C,C 吃 A,给若干句话,判断真假。
题解
扩展域并查集,把每个点扩展为 (x_{self}),(x_{eat}),(x_{enemy}),分别表示 (x) 的同类,食物和天敌,最后合并同类就可以了,需要注意的是如果 (x;eat;y),那么 (x) 的天敌是 (y) 的食物,需要考虑全面。
[JSOI2016]最佳团体
题意
给一颗树,选 (i) 就必须选 (fa[i]),每个点有 (c[i]) 和 (w[i]),求一个合法的点集,使得 (sumc) 和 (sumw) 的比值最大。
题解
涉及到比值,也就只能用 01分数规划 了,然后发现这道题就变成了求一个合法的最大值,直接树上背包就可以了,复杂度 (O(n^2logn))。
分数规划的柿子:(sum_{i=1}^k c[i] - ans imes w[i] <= 0)
[SHOI2011]双倍回文
题意
给定一个字符串,求最长双回文串。((n leq 10^6))
题解
这里给出两种做法:
-
manacher
考虑到本质不同的回文串的级别是 (O(n)) 的,所以我们在做 manacher 的过程中,每次暴力更新就可以了,具体做法是每次从当前的右端点暴力扩展,这样的复杂度是对的。 -
回文自动机
直接预处理一个 (trans[i]) 数组表示长度不大于 (len[i] / 2) 最长回文后缀,这个处理可能需要一些细节,然后建完后直接扫一遍就可以了。
[SCOI2008]城堡
题意
给定一个 (n) 个点的图,让你选择 (k) 个点,使一个点到你选择的点的最小距离最小。((n <= 50))
题解
这道题主要是用于模拟退火的做法,可以发现这是一个序列,那么我们退火的过程就变化为了随机枚举两个位置进行交换,然后大概就可以过了。
一般模拟退火主要用于求解最优方案,且对于一个方案计算其答案的复杂度不太高时使用。
[AHOI2018初中组]分组
题意
给一个序列,要求你选出若干序列,使得每个序列的权值连续且不出现重复权值,问你最短的序列最长是多少。
题解
很好的贪心,显然每次尽可能的取是不对的,我们用数组 (c[i]) 表示 (i) 这个位置出现的次数,那么如果 (c[i] <= c[i+1]), 那么我们可以继续取,否则我们就停止这次的选取,开始新的一次,这样的贪心显然是对的。
[TJOI2018]游园会
题意
一个串只能有 'N''O''I' 三种字符,给一个长度为(n(n<=15)) 的模式串,让你求有多少个长度为 (m(m<=1000)) 的串,满足 LCS 为 (k),且不能出现 "NOI" 这个字串的方案数。
题解
主要用到了DP套DP的思想,我们发现对于一个固定的串,LCS是单调不降的,而且模式串的长度只有 15,所以我们定义 (f[i][j][k]) 表示填到了第 (i) 个字符,当前模式串被匹配的状态为 (j),且这次填的字符为 (k) 的方案数,我们需要解决状态 (j) 的后继转移节点状态问题((tips):DP套DP解决的一般都是状态不好转移的情况),所以我们再设一个 (nxt[i][j]) 表示当前状态为 (i),且这次填第 (j) 个字符所能转移到的状态,然后这道题就解决了。
斐波那契数列
题意
求斐波那契数列的循环节((mod <= 2^{32}))
题解
本题有两种做法。
-
做法一
首先分解质因子,然后对于 (p^k) 求循环节,答案就是 ( ext{lcm})。
考虑怎么求 (p^k) 的循环节,首先有个结论 (G(p^k) = G(p) imes p^{k-1}),其中 (G(x)) 表示 (mod x) 的循环节,然后给出对于 (p > 5) 的循环节结论:(p mod 5 == (1,4),G(p) = p-1),(p mod 5 == (2,3),G(p) =2p-2),然后这题就随便做了。 -
做法二
首先猜测循环节不会大于 (6 imes mod),事实确实如此,然后有个东西叫做生日悖论,所以我们直接随机个几百次,每次放到 Hash 表里查询就行了,是不是非常简单,感觉实用性很高。
[TJOI2012]炸弹
题意
二维平面内有 (n) 个点,每个点可以连到与其的曼哈顿距离为 (r) 的点,且满足传递闭包,求至少需要几个初始点,才能连到所有的点。((n <= 10^5))
题解
暴力建图并查集是 (n^2) 的,所以我们还是考虑优化建图,这次的优化建图方式是扫描线加平衡树优化建图。
我们发现每个点的作用范围是个正方形,所以可以构建一条扫描线,我们的本意是想用扫描线使其一维满足条件,然后这个点向另外一维距离离它最近的两个点连边,这样每个点只用连两条边,是 (O(n)) 的,并且这样是正确的,因为需要维护最近的两个点,所以需要用平衡树维护。
[SNOI2017]炸弹
题意
平面上有 (n) 个点,每个点可以连到 ([l_i, r_i]) 的点,且有传递闭包性质,求每个点能连到多少个点。((n <= 10^5))
题解
不难看出是个Tarjan了,不过边数达到了 (O(n^2)) 级别,发现本题的连边范围是一段区间,所以可以线段树优化建图。
线段树优化建图就是建 (nlogn) 个点,然后在这些点中连边,是的点数和边数都达到 (nlogn) 级别。
考虑缩点后的做法,直接求并不是很好求,所以我们考虑维护出每个点的所能扩展到的最左和最右,然后遍历一遍 DAG 就可以了。
[Ynoi2019 模拟赛] Yuno loves sqrt technology III
题意
强制在线,维护区间众数。((n,m <= 10^5))
题解
复杂度 (O(nsqrt n)),考虑分块,首先预处理出任意两个块之间的答案,这个部分的复杂度可以 (O(nsqrt n)) 预处理,然后我们还需要用 vector 存下每个权值的所有位置,然后定义数组 (pos[i]) 表示 (i) 这个下标所在的 vector 里的位置,到此为止预处理完毕。
考虑一段询问 ([l, r]),我们先以 ([bel_l+1, bel_r-1]) 的答案作为答案,然后分别往左扫和往右扫,如果对于当前的一个位置满足 (vec[pos[i]+ans] <= r) 那么可以更新答案,单次复杂度 (O(sqrt n))。
当然如果可以离线的话,我们当然可以用回滚莫队了。
【模板】莫队二次离线(第十四分块(前体))
题意
给定一个序列,多组询问,每次求 ([l, r]) 内的 (popcount(a_i ; ext{xor}; a_j) == k(i < j)) 的个数。((n, m <= 10^5))
题解
发现可以离线,于是莫队,但是发现每次指针移动的贡献不是很好搞,所以考虑再次离线预处理出每次转移的贡献,我们设处理一次指针移动的复杂度是 (O(k)),那么复杂度可以从原来的 (O(nksqrt n)) 优化到 (O(nk + nsqrt n))。
现在只考虑从区间 ([l, r]) 移动到 ([l, r+1]),发现答案需要加上 ((r+1, [l, r])) 的贡献,这个因为满足可减性,所以可以变成 ((r+1, [1, r]) - (r+1, [1, l])),发现前面的部分可以 (O(nk)) 预处理,后面的那个可以用莫队做到 (O(nsqrt n)) 预处理,然后再跑一遍莫队就可以得到答案了。
对于预处理我们使用扫描线,我们可以利用异或的交换律来解决问题,定义 (c[i]) 为权值为 (i) 的异或后为 (k) 的个数,那么每次更新时只需要 (++c[a[i]; ext{xor};x]),其中 (x) 的 (popcount) 为 (k) 。
[Ynoi2019 模拟赛] Yuno loves sqrt technology II
题意
求区间逆序对。((n, m <= 10^5))
题解
显然可以莫队,然后单次转移用树状数组优化可以做到 (O(nsqrt n logn)),不过这道题只能拿20分,然后考虑如何优化莫队的单次转移。
不难发现莫队的转移可以二次离线,大概的方式等同于二次离线的板子题,所以复杂度可以优化到 (O(nsqrt n + nlogn))。
[APIO/CTSC 2007]数据备份
题意
数轴上给你 n 个点,你需要找出 K 个点不重复的点对,是的点对之间的距离之和最小。 ((n <= 10^5))
题解
考虑贪心,显然点对之间是独立的,不会出现相交和包含的情况,就是说每次只会选择相邻的两个点作为一个点对,所以我们可以采用贪心,但是你发现选了 (i, j) 就不能选择 (j, j+1) 和 (i-1, i),所以我们可以采用可撤销贪心来解决掉这个问题。
最小mex生成树
题意
让你求出一棵生成树,使其所有边权组成的集合的mex最小。 (n, m<=10^6)
题解
你发现mex不满足单调性这个性质,所以不大能二分答案,你考虑答案能否是 (x) 的判定条件,如果我们把所有为x的边权的边去掉,发现还可以形成一颗生成树,那么答案可以为 (x),当然可以会更小,但是符合题目要求。
考虑怎么求,因为没有单调性,所以你需要把所有的都 (judge) 一遍,所以我们可以用线段树分治来解决这个问题,就是用个扩展域并查集来解决这个问题,时间复杂度 (O(nlog^2n))。
[BJWC2010]严格次小生成树
题意
求出一棵严格次小生成树。 (n,m <= 10^5)
题解
我们先用 (Kruskal) 求出一棵最小生成树,考虑到 (Kruskal) 的本质是贪心思想,那么如果我们遍历每条未加入树中的边,然后找到它所形成的环中的最大的边,这个就可能是一棵次小生成树了。
我们可以选择LCT来维护或者倍增来维护最大值和次大值,维护次大值的原因是你需要严格的次小生成树。
区间本质不同子串个数
题意
给一个字符串,每次询问求 ([l, r]) 内的不同子串个数。((n,m <= 10^5))
题解
这个比求本质不同回文串还简单一点,显然先离线下来扫描线,然后考虑右端点为 (i) 的时的变化,发现变的只有以 (i) 为右端点的子串,如果我们知道它们上一次出现的位置,就可以树状数组维护差分了。
考虑怎么快速得到上次出现的位置,发现一定构成了一段段的等差序列,我们可以用LCT维护,每次 access 的过程中,每段实链一定是一段等差序列,所以我们直接用LCT维护就可以了,复杂度 (O(nlog^2n + mlogn))
[2021.4.6多校省选模拟31]亚特兰大
题意
每次修改一条边的边权,每次求路径上边权的gcd为1的路径条数,修改次数 <= 100。((n <= 10^5))
题解
首先可以想到莫比乌斯反演,这样就只用求是某个gcd倍数的答案了,暴力的话不难想到只留下边权为gcd倍数的边,然后用并查集维护,复杂度
(O(nQlogn))。
然后有一个优化就是发现可以用可撤销的并查集,这样平均下来复杂度可以做到(O(nQlogn*240)),可以拿70分。
我们发现只有100条边的边权会发生变化,我们可以更换加边顺序,最后加入这100条边,然后对于每个gcd,直接跑完100组询问,这样实现的好的话可以做到
(O(240*(n+100 * 100)logn)),然后就可以通过了。
当然许多其他做法也是可以过的。
[2021.4.5多校省选模拟30]最小表示
题意
求最小表示下的一个字符串的本质不同子串个数。 ((n <= 5 /times 10^4))
题解
这种题的大概思路都是更换hash的表示方法,然后用 (O(nlog^2)) 的后缀数组解决问题。
因为这道题的字符集很小,所以我们可以暴力求出来以每个位置为开头的字符集变换情况,然后一个子串的 Hash
拆分成每种字符集的答案之和。
后面直接套后缀数组板子就可以了,复杂度 (O(nlog^2*sum)),(sum) 表示字符集大小。
下面给出一种可以解决字符集很大的解决办法,我们定义一个位置i的hash值为它与上一个与它相同的字符的距离,这样可以用主席树维护,其余部分不变,复杂度可以做到
(O(nlog^3))。
[2021.4.5多校省选模拟30]环形划分
题意
给定一个 n 个点的无向完全图,请找出若干边不重复的三元环,使得剩下的边数 <= n - 1 (n <= 1000)
题解
就是一个构造题,考虑这样一个等式 (i + j + k equiv 0(mod; n)),发现对于一对 ([i, j]),(k) 是固定的,但是我们需要排除 (k == i) 的情况,发现不会超过 n - 1,然后本题完结。