前言
由于现在平时补题的时间不太多,可能看懂一个题解半小时,
然而,有时手头有别的事,就忘了实际ac&写博客,就这么把一个题咕掉了,
对于自己现在的水平来说,想出怎么做(思维难度)比怎么实现ac更重要,然而后者更耗时
所以打算开一个口嗨专栏,像是补一个题的过渡态,
把看懂题解的题先简写在这里,后续无论是否补上,也可以有一个回顾的地方
近期题目集锦
Codeforces Round #808 (Div. 1)
这场长沙一中出的cf,质量还是很高的
A. Doremy's IQ(贪心)
n(n<=1e5)场比赛,第i天难度ai(1<=ai<=1e9),你的初始智商是q(1<=q<=1e9)
如果你当前智商>0则有权参加比赛,每天你都可以选择是否参加比赛,
1. 如果不参加,什么都没发生
2. 如果参加,且ai>q,则当前智商q减1,ai<=q则什么都不发生
要求参加比赛数最大,输出这n天你的选择的01串
做法1:如果q>=n显然全取,否则最终智商一定为0,而{第i天减1,后面提前减为0,导致最后的有一些不选}显然是不如{把第i天减1的机会往后挪,这样有可能能有更多的可以选},所以,可以二分天数x,从x天开始每天都选。
做法2:如果q>=n显然全取,否则最终智商一定为0,显然降的机会发生在后面是更优的。逆序看待这个过程,把顺序的降认为是逆序的升,顺序的不操作认为是逆序的不操作,就可以直接贪心了,因为这样天数越小智商越高。
B. Difference Array(性质题-暴力)
长为n(n<=1e5)的不降序列,第i个值为ai,构造新数组b,,
将b数组排序后,记为新的a数组,可以发现a的长度减1,重复这个过程,直至a只剩一个元素
求最终a这个元素是几,特别地,
做法1:由于差分数组的元素种类数是根号级别的(考虑斐波那契数列),所以,可以用map暴力地维护,当前(key,value)表示元素key的值有几个。key值域上相邻会新增一个差分元素,相同元素做差分,只会新增0元素。
做法2:暴力排序,只对非0元素那一段排降序,排序时最多排入一个0,一样的效果。
复杂度证明:
C. DFS Trees(性质题-树上差分)
n(n<=1e5)个点,m(m<=2e5)条边的无向图,第i条边的边权是i,
对于每个点,执行给出的函数findMST(i),能得出最小生成树边集的输出1,否则输出0,
vis := an array of length n s := a set of edges function dfs(u): vis[u] := true iterate through each edge (u, v) in the order from smallest to largest edge weight if vis[v] = false add edge (u, v) into the set (s) dfs(v) function findMST(u): reset all elements of (vis) to false reset the edge set (s) to empty dfs(u) return the edge set (s)
输出最终的长度为n的串
做法:由于边权互不相同,所以MST唯一,考虑先构建出MST。
结论:若dfs过程中不存在横叉边,则这样找出的就是MST。
此题用在无向图这里,是个很好的性质题了。
树边:想保留的边。
前向边:又搜到儿子了。
返祖边:又搜到父亲了。
横叉边:又搜到兄弟了。
考虑官方题解给出的图:
根据MST性质,u-v不被取,说明u-v是这个环上权值最大的边,
如果从t/u/o/v开始搜,u-v都是返祖边,不被取;
而从a/b/c/d/e/f开始搜,u-v都是先被搜到的边,导致环上其他边成为横叉边,不符合题意
这表明,对于非MST上每条边(u,v)来说,只有两种情况符合题意:
1. 当v为根时,u这棵子树
2. 当u为根,v这棵子树
其他点都不符合题意,需要打个差分标记:
1. 若u和v是祖先关系,需要对u到v这一条链及这条链旁边的所有点标记
2. 若u和v不是祖先关系,需要对非u子树、非v子树以外的所有点标记
for(int i=1;i<=tp;++i){
int u=es[i].v,v=es[i].nt;
if(dp[u]>dp[v])u^=v^=u^=v;
int w=lca(u,v);
if(u==w){
--vis[v];
++vis[jump(v,dp[v]-dp[u]-1)];
}
else{
++vis[root];
--vis[u];
--vis[v];
}
}
题解打的是子树加标记,还是挺好写的
AtCoder Beginner Contest 259
Ex. Yet Another Path Counting(暴力)
n(n<=400)行n列的二维矩阵,第i行第j列有一个数字aij(1<=aij<=n*n),
你可以从某个格子出发,每次只能向下走一格或向右走一格,
求起点和终点数字相同的方案数,答案对998244353取模
做法:优雅的暴力
做法1:枚举相同数字对(x1,y1)和(x2,y2),然后用组合数算(类似卡特兰数,x步里选y步用于向右),点对数平方为O(n^4)
做法2:直接dp,dp[i][j]表示起点在(i,j)及左方/上方,当前走到(i,j)的方案数,枚举颜色,每次只对有这种颜色的标1,其他颜色标0,最后统计方案数总和,颜色最坏为n^2级别,每次遍历n^2,,为O(n^4)
考虑优雅的暴力,同颜色数>n时用做法2,<=n时用做法1,就降到了O(n^3),确实很妙
AtCoder Beginner Contest 258
Ex. Odd Steps(矩阵快速幂优化dp)
求满足以下条件的序列X的方案数,答案对998244353取模:
1. 序列X里每个元素都是奇数
2. 序列X元素之和恰为给定的S
3. 给定n个数a1,...,an,X的前缀和序列中不能出现这n个数里面出现过的数
题目满足n<=1e5,1<=a1<...<an<S<=1e18
做法:矩阵快速幂优化dp
考虑前缀和序列Y唯一映射出一个序列X,考虑朴素的前缀和序列的dp转移,
dp[i]=dp[i-1]+dp[i-3]+dp[i-5]+...,由于dp[i-2]=dp[i-3]+dp[i-5]+...,,所以dp[i]=dp[i-1]+dp[i-2],
考虑矩阵快速幂维护这个值,但是中间有一些值是要ban掉的,此时dp[i]为0,并不符合该式子,
如果dp[i]被ban了,手动搞出dp[i+1]和dp[i+2]之后继续转移,感觉比较难写(如果dp[i+1]也被ban了呢)
所以,考虑用dp值和前缀和sum两个值,来维护矩阵快速幂转移,
这样即使当前dp值为0,sum也能继续转移,每次对两个相邻元素ai和ai+1用矩阵快速幂
复杂度O(k^3*n*log1e18),k取2或3