题单题解
感觉只扔一个题单不太靠谱,再补一篇题解好了
题单
T1
一个普通的矩阵快速幂,如果做熟练的话看见这图的大小,这\(k\)的范围,矩阵快速幂无疑,主要是初始化难一点其实就是一个Floyd,初始化搞好之后直接就是一个裸的板子了。
T2
这道题的巧妙之处在于把数学问题转化为一个图论问题。先看操作一:如果有\((a_1,a_2)(a_2,a_3)(a_3,a_1)\),那么其中任意一个数都能自己加减二,如\(a_1\),\(a_1+1,a_2+1,a_2-1,a_3-1,a_3+1,a_1+1\)这样就能让\(a_1\)自己加减二,同理\(a_1\)换成任何数都可以,这里要注意,必须是奇数个点并且形成环才能这样办,所以每个奇数环上的数都能加减二,偶数个点为什么不行自己举个例子就明白了。再看操作二:如果有\((a_1,a_2)(a_2,a_3)\)那么可以知道\((a_1,a_3),a_1+1,a_2-1,a_2+1,a_3-1\)由此,\(a_1+1,a_3-1\),可见操作二是具有传递性的,如果把它们看做是一个联通块,那么这个联通块可以任意加1,减1,所以如果这个联通块需要加的值和需要减的值一样,那么就满足。于是我们只要把每个操作二都缩成点,每个操作一建边,然后开始判断每块联通块是不是满足题意。
判断方法为,如果未形成奇数环,即是二分图,则需要使联通块内相加的数与相减的数相等,因为只能加一减一,否则使需要变化的总数是偶数即可,注意自环也要判断,因为自环相当于\((a_1,a_2,1)(a_1,a_2,2)\)即\(a_1+1,a_2+1,a_2-1,a_1+1\),这样也能使任意数加减二,然后还有就是对于没有边连入的点,只有需要变化的值为0,才满足,因为没有边可以使它产生变化。
T3
简单的tarjan,注意到边是单向的,所以要么处在环上,可以一直走到它没办法产生蘑菇为止,要么只能走一次,所以把环上的边都缩到一起并让蘑菇一直再生直到为0能要为什么不要
T4
挺裸的分层图吧,建立\(K\)张图,然后跑最短路算法就行,这个思想感觉挺重要的
T5
\(K\)短路模板,为了要让方式数最多,贪心的去想,一定是每次取用花费最小的方案,所以跑一个\(K\)短路就行了,不过这题后来被卡了,所以要么特判要么就可持久化左偏树
T6
对于每个板子,有两个操作,然后想要从对角线的一端走到另一端的话,要么花费为0,不需要旋转,要么花费为1,需要旋转,可以建图跑最短路,不过效率不高,因为边权只有0和1,所以可以用双端队列BFS,当前边为0,从前边塞进去,为1,从后边塞进去,这样可以保证最近的一定在前边走
T7
显然是一个\(K\)短路,相信你们可以切了
T8
LCA的题目,模拟一下发现如果两条路径有交集,那么一定有一条路径的LCA在另一条路径上,而判断一个节点x,是否在路径s-t上需要满足
deep[x]>=deep[LCA(s,t)]
LCA(s,x)=x或LCA(t,x)=x,判断一下就行
T9
因为打标记后维护父亲节点比较难维护,尤其是并查集维护需要路径压缩,这就更是难上加难,但是合并操作却是十分容易,于是可以直接时光倒流,从后往前处理,离线搞一下就行。
T10
啊,树剖的板子不再解释了
T11
不难发现这是一棵树,然后它就又成了裸的树剖。。。。。。不小心放了两道板子
T12
看了题之后又说是树剖板子??再仔细看看,这次是在边上修改,好吧,和之前其实也差不多,只是需要特殊处理一下,把边权压到点上,考虑到每条边只连了一个儿子,所以把边权压到儿子上,而不是父亲,如果是父亲的话就会出现多条边打架的情况,所以不行。
修改和查询的时候注意在最后需要将路径一端的\(dfn+1\),因为这个点的点权是它与它父亲连边的边权,所以不能处理
T13
乍一看没有什么思路,但是一看数据会发现这要么是树,要么是基环树,基环树断掉一条边就是一棵树了,所以直接贪心的去跑一下就行,注意比较答案的时候要小心,字典序最小
T14
因为\(mod\)不为质数所以不能求逆元,考虑特殊性质,每个操作最多只被整除一次,所以可以开一棵线段树,然后转化为乘法操作即可
T15
这道题的意思应该是在\(l\)和\(r\)的区间内找出有多少个好的配对,这里好的配对是对于整个区间来说的,既然是对于整个区间,我们就不难想到找出好的配对的方法,所以我们可以先找出所有好的配对,然后用树状数组维护个数。
如何找出好的配对呢?我们先来分析什么叫好的配对,选定的两点间距离比其中一点到除对方外任意一点的距离都小,也就是说这两点差的绝对值最小,这样的话,这两个点在\(sort\)排序后一定相邻,这个很好推出,于是我们只要考虑这个点的另一个配对是左边的点还是右边的点,写一个判断就行了,注意特判\(1\)和\(n\)。
找出好的配对来,就又向答案接近了一步,现在我们只要进行更新就行了,这里的更新我们枚举左端点,上一步我们已经求出了好的配对\((l,r)\)如果查询的左端点在\(l\)的左边,那么从\(r\)开始向右就一定至少存在一个好的配对,所以让树状数组中的\(r\)对应的位置更新就行。注意我们要倒序枚举左端点,因为我们加入\(r\)后所产生的配对只能是在询问区间包含\(l\)的情况下才有效。
T16
线段树合并的板子,如果只有一种救济粮的话直接树上差分做就行了,可是这个有很多种救济粮,于是需要用一个可以合并的东西统计一下,权值线段树就能很好的做到这一点,记得动态开点。
T17
只需要考虑跨越子树的逆序对,因为在同一棵子树中的逆序对会在递归时被处理,如果对每一个点开一棵权值线段树的话,那么它子树中的逆序对为左子树的权值线段树的右子树大小乘右子树的权值线段树的左子树大小,可能有点绕,最开始被这里卡了,仔细想想也不是很难
T18
很显然,修建操作相当于合并两棵权值线段树,询问就是一个简单的查询,记得套一下并查集
T19
仍旧是权值线段树的问题,每次从叶子节点开始往上递归做一下就行,不过题意可能有点小坑,它的意思应该是先找到最多的颜色有多少个,然后把颜色个数是这个的编号都加起来当作这个点的答案
T20
可以很容易的发现一个小性质,收养所里不会同时有宠物和领养人,不然不管他们俩多不配都要配对在一起,所以只需要维护一个Splay就行,每次查找前驱和后继,比较哪个更优,不过易错点是如果前驱是-INF,那减掉前驱就会爆int然后出现错误,可以特殊处理一下
T21
裸的线段树了吧算是,不过。。。区间修改有两个,需要注意一下优先级,如果有覆盖标记的话在取反时应该优先给覆盖标记取反
T22
啊这题最正常的写法应该是强联通分量缩点,不过可以用bitset传递闭包一下,\(N^2\)的时间复杂度也可以而且好写
T23
这题的思维含量感觉挺高的,有的人想到了怎么做但是不会维护,有的人知道用什么维护不知道怎么做,首先要知道任取三个点,符合题意的最大半径是多少。
先考虑三点不共线的情况,由数学知识可以知道这三点能构成一个三角形。
因为三个圆最多只能相切,所以最大的直径就是最小的边。
三点共线的时候是一种特殊情况,但也和这个一样,最大的半径是任意两点间的最小距离的一半。
所以这个问题就简化成了,找出三个点连成三条边,使得这三条边中的最小值最大。这个比较好解决,把边权从大到小排一边序,当第一次有三个点连通时输出答案就行。
下面就是怎么判断三点是否连通,直接枚举三个点的话肯定会T掉,这时我们就要用到暴力神器\(bitset\),\(bitset\)存储二进制的状态,用第\(i\)个\(bitset\)的第\(j\)个二进制位表示\(i\)与\(j\)是否连通,然后我们枚举边,设边的起点为\(u\)终点为\(v\),如果能找到第三点\(k\)使得\(u\)与\(k\)连通,\(v\)与\(k\)连通,那么加上这条边的时候,这三点就连通了,否则把相应的二进制位设置成1。看起来很繁琐但是\(bitset\)的两个函数\(count\)和\(set\)能帮助我们很好的解决问题,前者的作用看名字就能看出来,\(set\)是将某位置设置成1,于是此题得解。
T24
裸的整数划分,但是一看见数据范围可能就挂了,需要用一下别的数学知识,众所周知数学是降低时间复杂度较好的方法之一,于是用五边形数定理,五边形数的生成函数的项数是\(\sqrt n\)的,所以可以在\(N\sqrt N\)的时间复杂度内解决该问题。
T25
讲真的我感觉可以打表找规律,一颗星星给的是证明过程。。。
T26,27,28
T29
看到异或,又看到可以选可以不选,于是不难想到用线性基,因为这是一棵树并且有多组询问,所以每次询问至多是\(log\)级别的,假设问两个点之间的距离怎么搞?用倍增\(lca\)搞一下就行,这个其实也一样,换成倍增线性基就可
T30
发现一个性质,如果走到一个环上走一圈再走回来,只有这个环上的权值可以被统计到答案,因为走向环的路径被走了两次直接异或掉了,所以把所有的环都扔到线性基里然后随便选一条从\(1\)到\(n\)的路径在线性基里取异或最大值了。
T31
同上,改成最小值了而已。。。。
T32
线性基强烈推荐此题
注意到什么样的数可以成为某个整数的平方,当且仅当它分解质因数为\(p_1^{c_1}p_2^{c_2}p_3^{c_3}……p_k^{c_k}\)后,对任意\(c_i\),都是偶数。所以其实对于每个数对答案的贡献来说,只有0和1两种,即考虑质因子的奇偶性,又因为这每个数最大也就是\(70\),所以可以把每个数都压成一个二进制数扔到线性基里边,最后的答案就是插入线性基能被异或成0的数的集合大小,把模型抽象出来就十分好写了。
T33
emm凸包其实,没什么可以说的,主要是利用叉积来判断一下就行,其实有用的也就正逆顺负这四个字,自己百度叉积看看吧。。
T34
自己找找规律,然后就没了。
T35
看到式子比较难可能有点慌,不过仔细看看会发现\(f\)函数就是让你求一下约数个数,然后直接枚举肯定是会炸,所以需要转化一下
令
不难想到
所以只要求出\(s_r\)即可,这个怎么求呢,考虑单个因子对答案的贡献,假设\(n\)的一个因子是\(p\),那么\(p\times1,p\times2,p\times3.......p\times\lfloor\frac{n}p\rfloor\)都会对答案有1个贡献,所以只要枚举一下\(p\)算贡献,得到
这样做是\(O(n)\)的,看起来足够优秀不过发现\(r\)最大已经达到了\(10^{14}\),所以这样做仍然会T掉,不难发现对于很多\(i\),\(\lfloor\frac{n}i\rfloor\)的值都是一样的,对于这样的\(i\),可以\(O(1)\)计算,这就是整除分块,于是时间复杂度进化到\(O(\sqrt n)\),可以A掉此题。
T36
\(d\)函数有一个性质
有了这个之后就可以把我们的式子化简为
根据上题的套路,可以先枚举因子\(i,j\)计算贡献,得到
然后就可以开始正式的莫比乌斯反演了,由
可以得到,
继续套娃,改为枚举\(d\)计算其它的贡献,得到
最后再整除分一下块就行了。
T37,38
这俩题其实是一样的,只是一个需要一点点容斥。
其实只需要算出
这两道题就能一起得到解决了。
考虑化简这个式子,如果前几道题认真看的话应该很快就能推出来。
整除分块搞一下就完了
T39,40
和上边差不多,改为枚举质数就行了,懒得写了。。。。
T41
很裸的多重背包,但是希望拿来练一下单调队列优化,而不是二进制拆分过了就走(虽然二进制拆分也能过)
T42
神仙DP题
定义\(DP_i\)表示治愈前\(i\)个村庄的最小死亡数,下面考虑状态转移,对于\(JYY\)来说,每个村庄它都有两个选择,治愈\(or\)先去别的再走回来治愈,治愈的话很好弄,主要考虑的就是略过它的情况,这时候如果依次枚举\(K\),效率应该是\(N^3\),程序吃不消,\(3000\)的极限数据我们最少也要压到\(N^2\)左右,所以接下来考虑优化。
优化其实也挺简单的,主要有一点很恶心,下边再说。(从这里开始默认\(j\)在\(i\)的前边)我们发现多出来的时间主要是用在了计算略过村庄再回来的死亡人数的计算,所以我们可以先考虑预处理出从\(j\)到\(i\)再从\(i\)到\(j\)然后又回到i这一过程中最少的死亡数,于是定义\(g_{i,j}\)含义为上述的来辅助我们的\(DP\)。
接下来考虑如何求解\(g\)数组,初始的话\(g[i][i]\)肯定是为0的,所以转移都应该从这个位置开始,即倒序,那么怎么转移呢,接下来就是很恶心的一个地方,计算经过的天数!很多题解里都没写到这个,这里详细计算一下。
对于\(g_{i,j}\),同样分两种情况讨论,救助或是略过,不管是救助还是略过,都避免不了经过一个区间,就是\(j+1\)到\(i\),所以这里可以分而治之,把\(j\)和\(j+1\)到\(i\)这两个分开,\(g_{i,j}\)的转移中应该需要有\(g_{i,j+1}\),这里又启示我们进行倒序循环,同样,不管救助\(j\)还是略过,从\(j\)走到\(j+1\)的这一天里,区间\(j+1\)到\(i\)这一段的村庄都会死亡(为村民默哀?)所以答案累加\(Sum_{j+1,i}\)这个可以由前缀和\(O(1)\)求出,到了点\(j+1\)后,\(j+1\)到\(i\)的死亡人数就已经被记在了\(g_{i,j+1}\)里,所以可以不用考虑,这是两种情况所共同具有的死亡人数,下面对两种情况分开讨论,如果救治\(j\)的人,那么区间\(j+1\),\(i\)的村民就要多死一天,即\(Sum_{j+1,i}\),不救治呢?因为同样的我们跑路的代价都记录在了\(g_{i,j+1}\)里边,所以不救治的代价就是在这段时间里\(j\)村死亡的人数,你可能问,别的村难道没有死亡的吗?当然可能会有,但我们已经记录了,所以这里不需要再次加入,首先算一下从\(j\)跑到\(i\)再跑回来所需要的时间,这里举个例子,从4到5要1天,4到6要2天,4到7要3天,所以显然跑路时\(j\)村死亡的人是\(2*(i-j)*a_j\),2是跑了两遍,\(i-j\)是刚刚推出来的,\(a_j\)是\(j\)村日死亡人数,那只有这些吗?当然不是,这只是跑路的代价,根据定义和题意,\(j+1\)到\(i\)这些村庄均被治愈且均在略过\(j\)后被治愈,所以一个村庄一天,一共就是\((i-(j+1)+1)*a_j\)天,于是我们的\(g_{i,j}\)就有了转移方程
\(g_{i,j}\)=\(g_{i,j+1}\)+\(Sum_{j+1,i}\)+\(min\)(\(3*(i-j)*a_j\),\(Sum_{j+1,i}\))
辅助进行转移的方程有了之后我们就可以进行\(dp\)的转移了,没错,下边还有很恶心的算时间。
对于每前\(i\)个村庄,都不可能直接求出他的最小值,所以要枚举中间点\(j\),即我治愈了前\(j\)个村庄,但是\(j+1\)被略过了,所以\(j+1\)的治愈是从\(j+1\)走到\(i\)再走回来时才被治愈,这一段的代价就是\(g_{i,j+1}\),治愈前j个的代价为\(dp_j\),直接累加答案即可,那么最硬核的东西就是\(i+1\)到\(n\)的这段区间,因为在当前阶段,这段区间内的人是不可能被治愈的,所以一天内的死亡人数是\(Sum_{i+1,n}\),天数呢?根据我之前所推导的,从\(j+1\)到\(i\)之间反复横跳一来一回一来需要天数\(3*(i-(j+1))\),治愈区间\(j+1\)到\(i\)需要时间\((i-(j+1)+1)\)。
这里不要忽略了一个地方,就是从\(j\)跑路跑到\(j+1\)时还有一天,所以总天数就是\(3*(i-(j+1))+i-(j+1)+1+1\),乘上每天死亡的人数就是最后的代价,累加答案。
至此,这道省选题就落下了帷幕……什么?你问我最后转移的时候没考虑略过\(i\)就是一路向右的情况?怎么可能,当我枚举到\(j=i-1\)的时候,就相当于转移了这种情况,是吧,所以这个算法是没有问题的,时间复杂度大致为\(O(n^2)\)可以A掉
T43,44,45
数位DP,按照套路设状态然后记忆化深搜就\(OK\)了
T46
考虑普通的\(DP\),令\(f_u\)表示切断\(u\)的子树中的所有点的代价,\(g_u\)表示从\(u\)到根节点的路径上最小的边权,分两种情况,如果\(u\)上边有资源,那么不管子树怎么样,\(u \)都要与根节点分离,即\(f_u=g_u\),否则就是\(min(g_u,\sum_{v|son}f_v)\),但是这样做显然会T的飞起,考虑怎么优化一下。
看到虽然询问次数很多但是询问的点不是很多,每次暴力DP的时候都把时间浪费在了搜索无关的点上边,如果把这些时间略掉就应该可以通过此题。
于是需要用到虚树,每次建一棵虚树,在虚树上边\(DP\),就可以完美\(AC\),注意一点就是虚树上边所有的点都需要与根节点断开联系,所以不存在\(f\)值为0的情况。
T47
数位DP
T48
代价和比较好算,枚举每条边,它的贡献就是他的边权乘上它被经过的次数,主要是最大的和最小的怎么算,这种具有最优子结构特性的问题不难想到\(DP\)解决,设\(f_u\)表示\(u\)到子树内最远的关键点的距离,\(g_u\)表示\(u\)到子树内最近的关键点的距离,然后\(DP\)搞一下就行。
T49
虚树DP板子
T50,51,52
数位DP
T53,54
这两道题都是斜率优化\(DP\)的题,感觉没什么可说的,毕竟这种\(DP\)一般转移方程应该不是很难,要注意在求斜率时考虑一下分母会不会为0,虽然除以浮点数的0不会报错但是交上去极有可能算错答案。
T55
这个\(DP\)也很有意思,发现如果正着做会有后效性,把它转一下就行了,至于怎么转,自己思考。
T56
可以先求出\(nxt\)数组,最短的重复子串一定是\(n-nxt[n]\),如果它不能整除\(n\),则说明没有重复子串,否则答案为\(\frac{n}{n-nxt[n]}\)
T57
这是一道思维好题,当然也十分考验我们对AC自动机的理解,先把Trie图建出来,然后标记每一个病毒串的结尾,如果能找到一个环使得这个环上没有被标记的点,那么就一定存在这么一个安全的串
T58
后缀排序板子
T59
断环成链然后后缀排序
T60
可以AC自动机,也可以KMP直接暴力,数据比较水(这题其实我是准备用来练习后缀排序的)
T61,62,63
典型的环形纸牌均分问题
可以这么去考虑,定义\(f_i\)为\(i\)向\(i-1\)传递的纸牌数,这样定义的话除了1以外别的值都没什么问题,所以推一波式子
然后相加移项推出
不难得出\(f_n\)的表达式
设\(g_n=\sum_{i=1}^{n-1}a_i-(n-1)\overline x\)
可知
使得
最小,就是让\(f_1\)最小,由小学数学可知,此时\(f_1\)为\(g\)数组的中位数
更深一点去说,\(g\)数组中有效的值其实只有\(n-1\)个,也就是说总有一个是0,这就能保证这个过程不是一个循环往复的而是有终点的
T64,65,66,67,68
思维题,自己想吧
T69,70,71,72
随机贪心也要看吗???!!