题3
10.24
P5658 [CSP-S2019] 括号树
这个实际上就是给定一个括号序列\(a\),然后对于每一个\(i\),来说,求出\([1,i]\)的所有合法括号子串(发现这个其实只需要求以\(i\)为结尾的合法括号后缀然后做前缀和就行了)那么只需要找出以\(i\)为结尾最短合法括号后缀就行,(设最短长度为\(g_i\),总数为\(f_i\)(设最短的位置在\(j\))),因为所有合法后缀一定是以\(j-1\)结尾的合法后缀再拼接\([j,i]\)就行。(当然包括\([j,i]\))所以\(f_i=f_j+1\)),所以,问题又成了求以\(i\)结尾的最短合法括号序列了。这个用\(g_i\)更新好了,挺好想的,然后没了。
10.28
唐题
给定一个序列\(a\),和一个数组\(k\), 定义函数\(r(x)=x\text{的因子数}\),需要构造出序列\(b\)满足\(\prod b_i | k\),使得\(\prod r(a_i*b_i)\)最大。
警示:一定要关注是连乘,所以给一项乘\(x\)相当于给最后的答案乘\(x\)。。。。。。。
这个其实很好想到了,就是把他唯一分解掉,那么\(r(a)=\prod (a_i+1)(a_i\text{是每一个质因数的系数})\),那么最终的答案就是\(\prod\prod(a_{i,j}+1)\),然后,因为\(b\)的限制,所以我们考虑一个质因数一个质因数的乘,我们发现给某一个\(a\)乘上一个质因数\(b\),就相当于给相应的\(a_{i,j}++\),所以最终给答案的贡献就是\(\times \frac{a_{i,j}+1+1}{a_{i,j}+1}=1+\frac{1}{a_{i,j}+1}\),那么肯定就是\(a_{i,j}\)越小越好,用堆维护一下就行。然后分析一下复杂度,就是\(O(n\log^2 n * 7)\),这个\(7\)是\(k\)最多的不同的质因子个数。\(\log^2\)是因为一个是堆的,一个是枚举每一个质因子在\(k\)中出现了几次
10.30
P5664 [CSP-S2019] Emiya 家今天的饭
关注到(看题解到)一个性质是,最多只有一个列会有多于\(\frac{k}{2}\)个数(绝对众数就是)。直接做没啥思路,然后就是正难则反环节,考虑容斥掉,答案就是:每行选不超过一个的方案数-每行选不超过一个且有一列超过了\(\frac{k}{2}\)。然后前面这个比较好算用一个\(dp\)算就行。然后就看后面这个。因为只有一列,所以直接枚举。假设枚举的是\(col\)列,那么设\(f_{i,j,k}\)表示在前\(i\)行中,第\(col\)列选\(j\)个,其他列选\(k\)个的方案树。那么最最后\(\sum_{j=[0,n],k=[0,n].j>k}f_{n,j,k}\)
转移就是
复杂度\(O(n^3m)\)炸飞了
又观察(看题解到)到\(j>k\)即\(j-k>0\),所以理论上只需要找到所有\(j-k>0\)的状态就可以了,那么状态就可以变为\(f_{i,j}\)表示在前\(i\)行里,选\(col\)列的数比选其他列的数多\(j\)个,然后转移差不多,不过复杂度变为了\(O(n^2m)\)。
总结:1. 转化题面,当正面做题不好做时,把题目进行转化,看这个是否好做(注意要是等价转化)2. \(dp\)过程中,要关注最终实际要求的值的性质,如这道题,有\(j-k>0\)的性质,所以就可以将维数减少,起到降低复杂度的目的。3. 性质性质性质性质!!!
11.16
P10680 [COTS 2024] 双双决斗 Dvoboj
考场上想了个假做法 似
- 暴力:像维护线段树一样, 为了求\(l,l+(1<<k)-1\)的答案,我们就去递归求\(l,l+(1<<(k-1))-1\)和\(l+(1<<(k-1)),l+(1<<k)-1\)的答案,这样修改复杂度为\(O(1)\),查询复杂度为\(O(n\log n)\),总体复杂度\(O(n^2\log n)\)
- 倍增:这个\(2^k\)很引导人向倍增哪里向,那么我们就设\(f_{i,j}\)为\(i,i+(1<<j)-1\)的答案,显然有\(f_{i,j}=\operatorname{abs}(f_{i,j-1}-f_{i+(1<<(j-1)),j-1})\),每次修改\(i\)都需要把\([1,i-1]\)的\(f\)都修改了,查询就直接访问就行了,这样修改复杂度为\(O(n\log n)\),查询复杂度为\(O(1)\),总复杂度就是\(O(n^2\log n)\)
- 倍增+根号分治:不难发现上面两个都不行啊,那么考虑如何优化,看到上面这个结构,要想根号分治了。那就想办法让\(n\to\sqrt{n}\)。发现其实倍增可以只维护长度小于\(\sqrt{n}\)的,然后查询的时候大于\(\sqrt{n}\)的,就除以二然后递归求就行了,这样查询和修改复杂度就都是\(O(\sqrt{n}\log\sqrt{n})\)了,总复杂度就是\(O(n\sqrt{n}\log\sqrt{n})\)可过
总结:碰到像\(1,2\)这样结构的复杂度就要考虑把\(n\to\sqrt{n}\)了。
11.17
P11024 [COTS 2020] 定序 Redoslijed
- 暴力:\(O(n!n)\)不说
- 稍微优化一下:我们发现直接做染色会面临先涂的颜色可能会被后面的颜色覆盖这样就很不好做,那么我们考虑正难则反,后涂的颜色不会被前面的颜色覆盖,所以我们倒过来看这个顺序。这样就对问题进行了一个转化:相当于我们要把最终状态的颜色一个个扣掉直到变成全空,不妨用\(0\)来表示空,那么,一个操作什么时候能够执行?答案是:每个格子处于\(0\)或和当前操作的颜色相同时。那么新的暴力也就出来了,就是每次暴力判断哪个可以操作,能操作就操作,然后把该操作对应的区间都改成\(0\)。code
- 最后也就是正解(题解)了。这个思路还差最后一个优化。上面的复杂度主要是用在暴力判断和区间修改上了。有一个 经典(题解说的) 优化是把判断的区间拆成线段树上的\(\log n\)个区间,每个区间要记录这个区间是否可以涂色了,如果所有都可以,那么整个区间也自然可以。那么考虑要维护什么东西。我们发现一个区间只有几种情况:1.全都是\(0\),2.除掉\(0\)后有一种颜色3.除掉\(0\)后有\(\ge 2\)中颜色。对他们分别进行设状态:\(0,c,-1(c\text{代表一种颜色是什么})\)。发现转移也很好转呀不说了。再说区间修改,每个数只有两种状态\(0,!0\),然后修改也只会把\(!0\to 0\),所以暴力改是没问题的,类似势能分析的复杂度感觉,只需要在向下递归的时候判断状态是否为\(0\)就行。ACcode
总结:1. 注意这个优化:如果之和一个区间的种类有关而与量无关的话,那么可以考虑用上面的优化方法2. 如果对一个数的修改最多只有常数的话,利用势能分析是可以判断暴力是否可行的。
P9102
这个 啊,我的做法有点过于麻烦了啊我去 我真服了。就是当\(j\ge maxn\)时,没必要真的存下来,都存放到\(f_n\)里面就行了,因为一定当\(j\ge n\) 时,一定能更新了我真是sb
11.20
P7961 [NOIP2021] 数列
这个属于是不看题解一万年也想不出来的那种。
一开始被这个有序集合限制住了,如果是无序的那一定是从大到小开始考虑,所以有序集合其实也可以这么做,套一个组合数就行了
不过想到这也不会。
怎么想到:? 设\(f_{i,j,k,p}\)表示在\(0~i\)中选了\(j\)个数,他们加起来的和\(0~i\)位共有\(k\)个\(1\),并且向\(i+1\)位进了\(p\)个\(1\)的权值和。那么转移就是枚举第\(i+1\)个数选了多少个,设为\(t\),那就能转移到\(f_{i+1,j+t,k+(p+t)\text{&}1,(p+t)>>1}\),然后组合数就是算在\(n-j\)里有多少个放\(i+1\)的方式,就是\(\binom{n-j}{t}\),然后贡献,因为是和,所以就是\(f_{i,j,k,p}*{v_{i+1}}^t\).转移方程就随之出来了。
f[i + 1][j + t][k + (p + t & 1)][p + t >> 1] += f[i][j][k][p] * C(n - j, t) % mod * v[i + 1][t] % mod;`
没了。
总结:1. 看到有序的思考是否可以通过套组合数的方式变成无序的。2. 注意转移状态是一定是合法的转移到合法的,比如这里的\(p\)一定要\(\ge n\),(因为最多才能进位\(n\)个\(1\)),所以写for时就要限制\(p\ge n\),否则会出错。3. 关于二进制的一些dp,还有数位dp还不会要多做两道
11.22
CF1706E Qpwoeirut and Vertices
首先能想到的是$ans(l,r)=max_{i=l}^{r-1}ans(i,i+1)&,然后这个很显然就可以用st表做。问题在于 \(ans(i,i+1)\)怎么求。那就到题解了
我们发现\(ans(i,i+1)\)就是表示加了\(ans(i,i+1)\)条边以后\(i,i+1\)在同一个连通块里。然后我们顺序放边,每放一条边就会合并两个连通块,这时就要考虑更新答案了。那就免不了要遍历连通块中的元素了。根据启发式合并的思路,我们就应该遍历小的连通块,这样,每个元素最多会被遍历\(\log n\)次,这样也就保证了总体复杂度为\(O(n\log n)\)了。那么这道题也就做出来了。
总结:把题意进行转化,转化成可做题。多做题,多积累思路。这道题的思路就是把最小值问题转化成了连通性问题。该方法能在关于两点之间路径的边权上有作用
11.23
CF1416D Graph and Queries
这个感觉是\(kruskal\)重构树的板子,就是把删边的过程反过来看,当作从后向前做加边,这样就能建出一棵重构树,然后发现删边的操作就是删去一个点,然后每次询问就是问一个连通块(也就是一棵子树)里最大的点是谁,这个用线段树就行了。所以我们只需要维护每个点的根是谁(因为删点后就可能变成森林)。这个看也能用线段树做。所以这个题就没了
11.24
P10795 『SpOI - R1』Lamborghini (Demo)
嘻嘻第一次独立写紫(虽然是板子。
这个就是,关注到\(tx\)互不相同,然后还要满足\(tx\)必须是最小的限制。那么能得到一个思路就是反着放点,然后这个点和之前的点构成的联通块中,任意两个点的路径经过这个点都满足这个点是最小点,但是这个连通块中不能有\(t\)比他小的,所以自然的可以想到把\(tx\)放到边上\(u\Leftrightarrow v\) 的\(t\)就是\(\min(t[u], t[v])\)。然后把边从大到小放到连通块里,这样就能保证连通块里所有的点都比\(tx\)大了。然后贡献呢,就是对每一个连通块建一个权值线段树,然后访问加入边的两个端点所在连通块的线段树,根据题意的要求更新一下答案就行了,加边就是合并两个连通块,也就是要合并两个权值线段树,嗯,所以要写线段树合并。最后要思考的就是,在上述给边赋值的过程中,不同边的权值是可以相同的,但是合并的顺序不会影响算法的正确性。
总结:如同CF1706E Qpwoeirut and Vertices。该题就是把最小值这个限制改成了加边后是否联通的问题,再次印证了这个做法的应用场景:图中限制或要求的值是一个最值时,可以考虑通过顺序加边来转化成连通性问题
11.25
sjz2505
这个就是能把\(a_i a_j < a_i + a_j\)转化成\((a_i-1)(a_j-1)<1\),然后这样又因为\(a_i,a_j\)都是整数,所以只要异号就一定小于\(1\),当然\(0<1\),所以记录一下\(>1,=1,<1\)的个数就行了,时间复杂度\(O(n)\)
总结:
\(n=1e6\)想\(O(n)\),我真是服了,写\(O(n\log n)\)以为能过。。。。。

浙公网安备 33010602011771号