Codeforces Daily (Round 370-410)

本文是作者关于 Codeforces 平台 370 至 410 场比赛的题解汇总,详细介绍了每场比赛的多个题目解题思路,包括贪心算法、动态规划、线段树、博弈论等多种算法的应用。文章主要记录了解题过程和关键点,便于读者理解和复盘。
摘要由CSDN通过智能技术生成

前言:人菜就要多刷题

本题解更多是记录CF练习日常,顺便写写题解
由于技术原因,Dvi.1的题和个别EF会慢慢补
有的题目之前写过,这些题的题解会有些简陋
有的思路难以描述,这些题的题解会有些简陋
≤ \leq ≥ \geq ∑ a a \sum\limits_{a}^a aa a b \frac{a}{b} ba ( a b ) \binom{a}{b} (ba) a ≡ 1 ( m o d m ) a\equiv1\pmod{m} a1(modm)
       

---------------------------------------------------------------------------

场次:370 (Div. 2)

C:保证三角形下贪心搞搞就行
D:算出打完以后每种分数的方案数,然后枚举分数即可。dp[i][j]表示i次以后分数为j的概率, d p [ i ] [ j ] = ∑ x = j − k j + k d p [ i − 1 ] [ x ] dp[i][j]=\sum_{x=j-k}^{j+k}dp[i-1][x] dp[i][j]=x=jkj+kdp[i1][x],这些暴力搞就行了,需要滚动数组
E:思考下如何用线段树解决这个问题。线段树维护某个区间从左端开始走到最右的概率(记为L),从最右开始走出的概率(记为R)。现在假设要合并两个区间(左区间为 L 1 , R 1 L_1,R_1 L1,R1,右区间为 L 2 , R 2 L_2,R_2 L2,R2,合并后的区间为 L 3 , R 3 L_3,R_3 L3,R3),则
L 3 = L 1 ∗ L 2 + L 1 ∗ L 2 ∗ ( 1 − L 2 ) ∗ R 1 + L 1 ∗ L 2 ∗ ( ( 1 − L 2 ) ∗ R 1 ) 2 + . . . L_3=L_1*L_2+L_1*L_2*(1-L_2)*R_1+L_1*L_2*((1-L_2)*R_1)^2+... L3=L1L2+L1L2(1L2)R1+L1L2((1L2)R1)2+...
(理解起来就是左过到右,然后回来,在过去,再过去。。。)这是一个等比数列
R 3 = R 2 + ( 1 − R 2 ) ∗ R 1 ∗ L 2 + ( 1 − R 2 ) ∗ R 1 ∗ L 1 ∗ ( 1 − L 2 ) ∗ R 1 + ( 1 − R 2 ) ∗ R 1 ∗ L 1 ∗ ( ( 1 − L 2 ) ∗ R 1 ) 2 . . . R_3=R_2+(1-R_2)*R_1*L_2+(1-R_2)*R_1*L1*(1-L_2)*R_1+(1-R_2)*R_1*L1*((1-L_2)*R_1)^2... R3=R2+(1R2)R1L2+(1R2)R1L1(1L2)R1+(1R2)R1L1((1L2)R1)2...
(理解起来也是,过去再过来,再过去),从第二项开始也是等比数列
这样就都能维护了

---------------------------------------------------------------------------

场次:371 (Div. 2)

C:一个数字每位只分奇偶,那么我们就可以把数字转化一下,查询就是查询一样的值有几个
D:二分,先求出横坐标的,然后是纵坐标的,总共八次二分,写起来还是有点麻烦的。可以这样写的简单点,首先我们把坐下的矩形求出来,然后二分上面的矩形,此刻,如果我们二分第二个矩形的区域包含第一个矩形,我们可以手动让返回的值减一,这样二分会好写很多
E:先说怎么求最长非严格上升,我们能发现,最大值必定是取原数组中的某个值最优, d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i为原数组第j大时的最小花费,那么 d p [ i ] [ j ] = ∣ a [ i ] − b [ j ] ∣ + m i n ( d p [ i − 1 ] [ k ] ) ( k &lt; = j ) dp[i][j]=|a[i]-b[j]|+min(dp[i-1][k])(k&lt;=j) dp[i][j]=a[i]b[j]+min(dp[i1][k])(k<=j).当我们令每个 a [ i ] a[i] a[i]-= i i i后,求出的非严格上升的花费就是严格上升的花费

---------------------------------------------------------------------------

场次:372 (Div. 2)

C:因为只要可行解且 a [ i ] a[i] a[i]为i的倍数,所以我们可以假设 a [ i ] a[i] a[i] i 2 ( i + 1 ) 2 i^2(i+1)^2 i2(i+1)2,然后化一化公式即可
D:如果直接跑出的最短路小于l,则输出NO,如果删掉的边都赋为1跑出的最短路为大于l,则输出NO,其他情况就都是YES了,我们只要枚举删去的边,将边权改为 l − d i s t [ t ] + 1 l-dist[t]+1 ldist[t]+1即可(因为你总会改到最短路上只有一个被删去的边的) O ( n m log ⁡ n ) O(nm\log n) O(nmlogn)
E:显然树分治,假设我们构建完重心后,在当前dfs的子树中得到一个深度为dep,从上到下值为b的链,那么就需要在其他子树中存在一个a,使得 a ∗ 1 0 d e p + b = 0 ( m o d M ) a*10^{dep}+b=0(mod M) a10dep+b=0modM,即 a = − b ∗ i n v ( 1 0 d e p ) a=-b*inv(10^{dep}) a=binv(10dep),这样就能树分治了。(注意a是下到上的,那么我们在dfs时要记录两个值,同时为了记录全部答案,要正着跑一次再到着跑一次)

---------------------------------------------------------------------------

场次:373 (Div. 2)

C:找到小数点后第一个大于等于5后模拟即可
D:没D
E:斐波那契数列可以矩阵快速幂求。我们开一个矩阵类的线段树,那么给区间加一个值相当于给线段树的每个的值乘一个矩阵,这样就可以维护了,同理,lazy也需要是矩阵类的,可以将lazy初始化为单位矩阵

---------------------------------------------------------------------------

场次:374 (Div. 2)

C:dfs剪剪枝就行了
D:容易想到总乘积为负数时更优,所以要让负数个数为奇数,这里贪心地取正数最小和负数最大的进行改变,当乘积为负时,可知道依旧是变大最小正数或变小最大负数更优,贪心即可
E:设 d p [ i ] dp[i] dp[i]为走完第i个灯的唱歌数,那么 d p [ i ] = max ⁡ j ≤ i ( d p [ j ] + ( r i − m a x ( g [ i ] + t , l i ) ) / p ) ) dp[i]=\max\limits_{j\leq i}(dp[j]+(r_i-max(g[i]+t,l_i))/p)) dp[i]=jimax(dp[j]+(rimax(g[i]+t,li))/p))因为一个灯下可以不唱,所以 d p [ i ] = d p [ i − 1 ] dp[i]=dp[i-1] dp[i]=dp[i1],所以更新有效区间为 x ≤ j ≤ y x\leq j \leq y xjy其中x为最后一个 g [ x ] + t ≤ l i g[x]+t\leq l_i g[x]+tli,y为最后一个 g [ y ] + t ≤ r i g[y]+t\leq r_i g[y]+tri。因为各个灯没有相交,所以 g [ y ] + t &lt; = l i + 1 g[y]+t&lt;=l_{i+1} g[y]+t<=li+1,所以灯i的 x i , y i x_i,y_i xi,yi x i + 1 , y i + 1 x_{i+1},y_{i+1} xi+1,yi+1不相交(除了 y i y_i yi== x i + 1 x_{i+1} xi+1这种情况),这样我们就能线性处理了

---------------------------------------------------------------------------

场次:375 (Div. 2)

C:次数是n/m,那么暴力统计次数然后改就行了
D:dfs出每个湖大小然后排序填即可
E:要求入度和出度相同,首先度数为奇数的点必定不可能达到要求,如果我们让每两个奇数的点连一个边,这样图就没有了度数为偶数的点,那么就存在欧拉回路,这样每个点出度等于入度,这样每个度数为偶数的点就都满足了要求
F:为了少用度数,我们先把两端不是s或t的边能连就连,这个用并查集。然后我们现在剩下三个部分,s,t,一堆集合,那么这些集合有三种,第一种是只能连s,第二种是只能连t,第三种都能连。如果有第三种,那么很明显不让st连边更优。否则就只能用st边,然后剩下的集合贪心即可。不是很好写,注意细节,想不出好方法就码农吧(似乎不容易wa在大数据)

---------------------------------------------------------------------------

场次:376 (Div. 2)

C:并查集下,然后让每个集合中出现最多的颜色不变,其他都变就行了
D:因为每旋转c次就会循环,所以我们只需要求0到c-1时串之间的关系即可。对于每两个相邻的串a,b,我们都能求出这a<b对应的是0到c-1中的哪些区间。在求出所有相邻的串的对应区间后,利用差分的思想,记录每个区间开头和结尾,就能知道当开关旋转x时,会有多少个相邻的串满足上小于下,这样就能知道有没有符合条件的区间了
E:dp[i]表示,在i到n中任选一个点作为先手第一步取的地方的最大差值,然后设a数组为给定数组的前缀和,那么
for(int i=n-1;i>=2;i–){
        dp[i]=max(a[i]-MAX,MAX);
        MAX=max(a[i]-MAX,MAX);
}
这两步的意思就是,尝试用直接取第i个来取到最大值,a[i]-MAX是因为,当选了i,那么之后的最佳取法都会取反(自己画画图就明白了)。所以,每次能更新dp的值,就代表了这是一个决策点,比如:我在2,5,7处更新了dp值,那么博弈中,先手会选2,7,后手会选5。答案则是dp[2]
F:统计各个数出现次数,然后搞个前缀和,用类似素数筛那样的去统计答案。假如统计x的答案,那么就类似素数筛那样,先x,然后2x,然后3x。。。统计到了i,那么
r e t + = ( a [ i ] − a [ i − 1 ] ) ∗ i ; ret+=(a[i]-a[i-1])*i; ret+=(a[i]a[i1])i; r e t + = ( i − x ) ∗ ( a [ i − 1 ] − a [ i − x ] ) ; ret+=(i-x)*(a[i-1]-a[i-x]); ret+=(ix)(a[i1]a[ix]);
a数组就是之前说的数量的前缀和,这两步的意思就是i这个点的直接统计答案,位于i-x到i之间的数我当作i-x归入到答案里面

---------------------------------------------------------------------------

场次:377 (Div. 2)

C:判一下每种情况就行
D:二分答案即可
E:用插座去匹配电脑,能匹配的就直接匹配,然后将剩下的每个插座都加上一个适配器,然后继续匹配,直到最后剩下的插座功率都是1。为什么贪心是对的?因为插座能产生替代关系,就证明这几个插座从某几个适配器开始存在倍数关系,写一写模拟下就发现这种情况下先取谁其实沒影响(所以根本不需要sort)
F:边双联通分量的边变为有向边,可以构造为强联通。将原图求出多个边双联通分量,形成一颗树,由于n个点,n-1条边,那么必定有一条边无出度,意味着这个点没有增加,而其他点都可以间接或直接指向他,意味着这个点变成了最小的点。希望最小的点尽可能的大,那么就将边双联通分量最大的点作为这个点就好了。这样构造方法就明了了

---------------------------------------------------------------------------

场次:378 (Div. 2)

C:因为a[1]只能属于b[1],所以可以得知b[i]对应的是哪段的a,那么剩下怎么合成了,我们希望尽可能少地出冲突,那么最优就是每次选择数列中最大的数进行移动,然后模拟这个过程,因为n很小,所以 n 2 n^2 n2乱搞就行了
D:排序后贪心
E:模拟题意,发现在做往返运动,且每向左遇到U就会折返,向右遇到D就会折返,而自身的字母决定了先向右还是想左,这样题意就变成了求与i相邻的,左端最近的x个U的距离,右端最近的y个D的距离,此刻的x,y决定于左端U和右端D谁多,以及开始的方向。这样算出x,y后,只要再加上自己从左端或右端出去的部署步数。这个过程会发现,左端取的U的位置是呈现单调性的,同理右端D也是呈现单调性的,这样用两个队列模拟即可。
F:因为可以把边权变成负,那么最优解一定是只减一条边。这意味着答案与最小生成树只会差一条边。那么可以先求出最小生成树,用剩下的边更新这棵树,当更新的边为(u,v)时,最小生成树上对应了(u,v)这条路经。所以需要求出(u,v)上最大的边是哪一条,这个可以用倍增或者树剖解决(因为要输出路径,所以不管倍增还是树剖,都麻烦疯了)

---------------------------------------------------------------------------

场次:379 (Div. 2)

C:第一种按b排序,第二种按d排序,然后将第一种按照a递减这样放到栈里面(如果当前的a比栈顶的a小,就不放进去),这个贪心的意思就是,当b变多,那么a肯定变小。然后扫第二种,如果栈顶的b加上自己的d大于s,就让栈顶弹出。这个贪心的意思是,因为已经按照d排序连,如果当前元素都不能满足,那么后面元素也肯定不能满足。操作的时候更新答案就行了
D:能将军只有八个方向,并且要求前面不能有阻拦,那么按照每个棋子距离白王的距离排序,然后判断处于哪个方向,判断有没有阻拦,棋子类型是否符合就行了
E:很明显要缩点,那么最少操作次数就是缩完后的图的直径
F:有个性质:A+B=(A&B)+(A|B) 那么 b [ i ] + c [ i ] = n ∗ a [ i ] + ∑ j = 1 n a [ j ] b[i]+c[i]=n*a[i]+\sum_{j=1}^{n}a[j] b[i]+c[i]=na[i]+j=1na[j] 2 ∗ n ∗ ∑ i = 1 n a [ i ] = ∑ i = 1 n b [ i ] + ∑ i = 1 n c [ i ] 2*n*\sum_{i=1}^{n}a[i]=\sum_{i=1}^{n}b[i]+\sum_{i=1}^{n}c[i] 2ni=1na[i]=i=1nb[i]+i=1nc[i],这样就能推导出a数组了,但是这样推导出的a数组不一定是正确的,因为n个n元一次方程可能无解,那么我们对每一个a[i]验证一下,用二进制的位来算贡献即可

---------------------------------------------------------------------------

场次:380 (Div. 2)

C:二分最小油量然后用公式check即可
D:有连续b个0就要炸,且炸最后一个,算出可能的位置总数,然后减去a-1
E:因为每个点都写进去了,所以数组排序后应当是一个上升且相邻元素差值小于1,然后就可以贪心处理连。当一个数大于上一个超过1时,有两种选择,一种是改变之前的数(比如0 1 1可以改为0 1 2),一种是选数组最大的数改,很明显后一种贪心更优
F:根据官方题解
I l , r , k = m a x ( z l + k , r , k + ∑ i = l l + k a [ i ] , z l + k + 1 , r , k + 1 + ∑ i = l l + k + 1 a [ i ] ) I_{l,r,k}=max(z_{l+k,r,k}+\sum\limits_{i=l}^{l+k}a[i],z_{l+k+1,r,k+1}+\sum\limits_{i=l}^{l+k+1}a[i]) Il,r,k=max(zl+k,r,k+i=ll+ka[i],zl+k+1,r,k+1+i=ll+k+1a[i])
Z l , r , k = m i n ( I l , r − k , k − ∑ i = r − k r a [ i ] , z l , r − k − 1 , k + 1 − ∑ i = r − k − 1 r a [ i ] ) Z_{l,r,k}=min(I_{l,r-k,k}-\sum\limits_{i=r-k}^{r}a[i],z_{l,r-k-1,k+1}-\sum\limits_{i=r-k-1}^{r}a[i]) Zl,r,k=min(Il,rk,ki=rkra[i],zl,rk1,k+1i=rk1ra[i])
其中 I l , r , k I_{l,r,k} Il,r,k的意思就是 I I I在[l,r]中,至少选k个所能获得的最大值,式子不难理解,但是需要优化。
上式中k的取值为 1 + 2 + 3 + . . + k ≤ n 1+2+3+..+k\leq n 1+2+3+..+kn,所以k取到90就够用了。
为了进一步节省空间,可以使用两人的所取的差值来表示r,当两人取到最大差值时,为 1 + 1 + 2 + 2 + 3 + 3 + 4 + 4 + . . . ≤ n 1+1+2+2+3+3+4+4+...\leq n 1+1+2+2+3+3+4+4+...n 所以取到180足够了,因此空间就变成了dp[2000][180][90],这样就足够了

---------------------------------------------------------------------------

场次:381 (Div. 2)

C:要求的mex要最大,那么最小的mex值肯定是区间最小的那个,然后,就是1~n的数串赋值0~n-1然后模一下最小的mex即可
D:对于一个点,能产生贡献的只能是自己的祖先,可以倍增地去寻找最远到达的祖先,那么这个点到最远祖先的贡献都要加1,用树上差分实现
E:设原数组为a,创建一个长度为n-1的数组b, b [ i ] = a [ i + 1 ] − a [ i ] b[i]=a[i+1]-a[i] b[i]=a[i+1]a[i],这样,题目变成了最长的一段正数跟着一段负数的长度。尝试用线段树合并解决这个问题,开5棵线段树,分别表示左端连续最长负数长度,右端连续最长正数长度,左端最长答案,右端最长答案,总体最长答案。剩下怎么更新不难但难以描述,不懂可以看我的代码

---------------------------------------------------------------------------

场次:382 (Div. 2)

C:按照贪心的想法,会发现是一个斐波那契数列,那么直接模拟即可
D:歌德巴赫猜想,偶数直接给,奇数判一下,奇数有三种情况,自身为质数,自身-2为质数,以及剩余的条件,因为偶质数只有2,所以这个方法最优
E:dp[x][i]表示:当 i ≤ K i\leq K iK时:合法状态,表示i的子树中到根的最近黑点距离为j的方案数.当 K &lt; i ≤ 2 K K&lt;i\leq 2K K<i2K时:不合法状态,表示i的子树中,需要在上面补充黑点,且这个黑点到i的距离应该至多为(2K - j + 1).每遍历完一次数组时y,就可以用这颗子树y的信息更新dp[x]。枚举x的距离为i,y的距离为j。
        当 i + j &lt; = 2 ∗ k 当i+j&lt;=2*k i+j<=2k意味着所有节点都能满足题目要求,这样对答案的贡献为 f [ m i n ( i , j + 1 ) ] + = d p [ x ] [ i ] ∗ d p [ y ] [ j ] f[min(i,j+1)]+=dp[x][i]*dp[y][j] f[min(i,j+1)]+=dp[x][i]dp[y][j](j+1是因为y离x的距离为1)
       当 i + j &gt; 2 ∗ k i+j&gt;2*k i+j>2k时,意味着有的点不满足题目要求,想要满足,就必须把x的祖先填黑,那么 f [ m a x ( i , j + 1 ) ] + = d p [ x ] [ i ] ∗ d p [ y ] [ j ] f[max(i,j+1)]+=dp[x][i]*dp[y][j] f[max(i,j+1)]+=dp[x][i]dp[y][j],取max的意义可以根据上面对dp的数组的意义理解
       现在只剩下初始化的问题了,我们初始化dp[x][0]=1,意思是将自己图和,dp[x][k+1]=1意味着要有和x距离在k-1之内的祖先涂黑

---------------------------------------------------------------------------

场次:#383 (Div. 2)

C:暴力跑一下每个点要几次,然后求个lcm就行了
D:并查集维护出每个团体,然后把团体的每个人的w和b都加起来形成一个新的人(即重量和体积分别是 ∑ w ∑ b \sum w \sum b wb),问题就变成了,每个团体选一个,这个和正常背包比起来只是改了改for,就是
        f o r ( i n t for(int for(int  j j j= t o t w ; j ≥ 0 ; j totw;j\geq 0;j totw;j0;j- - ) ) )
                f o r ( i n t for(int for(int  k k k= 0 ; k 0;k 0;k< v [ i ] . s i z e ( ) ; k v[i].size();k v[i].size();k++ ) ) )
第一层for保证是01背包,第二层for保证每个团队只选一个
E:让情侣的不同,让相邻的不同,然后根据这些关系建边,跑一次就行了(很奇怪并不存在-1的情况,而且为什么直接设相邻的不同就能行了)

---------------------------------------------------------------------------

场次:#384 (Div. 2)

C: 2 n \frac{2}{n} n2 可以被拆分为 1 n + 1 n + 1 + 1 n ∗ ( n + 1 ) \frac{1}{n}+\frac{1}{n+1}+\frac{1}{n*(n+1)} n1+n+11+n(n+1)1 ,n=1时无解,其他输出答案即可
D:dfs的时候,即设到了点x,统计一下x的子树的大小a以及这子树x的所有子数中的最大值b,用 m a x ( a , b ) max(a,b) max(a,b)更新答案即可,同时向父亲返还 m a x ( a , b ) max(a,b) max(a,b)这样就能保证更新答案的最大值没有交集
E:二分八个数中最小的出现次数,然后状压DP去统计判断是否合法。dp[i][j]表示当处理到第i位且状态为j时,对于二分的x能获得的最长的子串长度。可以先把这8个数字出现的位置预处理出来,这样dp的时候,可以这样
i n t int int  p o s = u p p e r b o u n d ( a l l ( v [ k ] ) , i ) − v [ k ] . b e g i n ( ) ; p o s + = m − 1 ; pos=upper_bound(all(v[k]),i)-v[k].begin(); pos+=m-1; pos=upperbound(all(v[k]),i)v[k].begin();pos+=m1;(m为二分的值)
意思就是需找i之后第m个k的位置,然后判断下是否合法就可以更新了。因为相邻的可以多一个,所以还需要尝试多放一个k,步骤和之前的相同
在二分的同时更新答案,这样二分结束了,答案就已经更新好了

---------------------------------------------------------------------------

场次:#385 (Div. 2)

C:并查集维护每个集合的大小,然后让k个点不联通的情况下算边数量即可
D:利用二进制,每个元素的下标按照二进制询问,这样一共询问 2 ∗ l o g n 2*logn 2logn次,这个做法可以保证每个元素至少在一次分组中和零元素不在同一组中。
E:状压dp, d p [ i ] [ j ] dp[i][j] dp[i][j]表示在i这种状态下,红色节省j个的情况下,蓝色最多节省多少了。通过计算一个状态的红色蓝色个数,就能得到状态转移了。转移方程就是:
d p [ i ∣ ( dp[i|( dp[i( 1<< k ) ] [ j + r e d ] = m a x ( d p [ i ∣ ( k)][j+red]=max(dp[i|( k)][j+red]=max(dp[i( 1<< k ) ] [ j + r e d ] , d p [ i ] [ j ] + b l u e ) k)][j+red],dp[i][j]+blue) k)][j+red],dp[i][j]+blue)
(其中K为新买的卡片,red为在i这种状态下红色节省个数,blue为蓝色节省个数)
那么知道了节省红色为x个时蓝色最多的节省个数,就能得出总体花费回合数,就能更新出答案了

---------------------------------------------------------------------------

场次:#386 (Div. 2)

C:如果人车相遇,则时间为谁先到的时间,否则则为人的时间
D:在保证不超过k个的情况下,优先放多的,这样能够保证贪心策略最优
E:首先重复的是肯定要删除的,所以先将需要删除的拿出来,同时将出现过的的数字在map中打标记,然后枚举对方卡片,从小到大贪心,因为最终的奇偶数是确定的,所以知道奇数缺几张,偶数缺几张,这样贪心地去取即可
F:如果从左向右遍历每一个歌曲,当遍历到的每首歌,如果我们能够得出存在这首歌的时候,向左最多能够听几首歌,价值是多少,那这样更新出的答案必定是最优的。我们能够发现,随着我们遍历的歌曲位置的增加,最左边位置的歌曲也在增加。所以可以使用set维护歌曲信息,一个set维护花费最大值(保存听完的歌曲),一个set维护花费最小值(保存听一半的歌曲),这样就能很好维护上面的信息了
G:如果某层父亲多于儿子,则必定产生叶子,这样可是预先判断是否合法,然后将答案减去这些必定产生的叶子个数,那么对于剩下的需要产生的叶子,贪心处理即可,还有最后一个问题,加入贪到最后还是无法产生足够的叶子,这个保存答案判断是否合法即可

---------------------------------------------------------------------------

场次:387 (Div. 2)

C:开俩set,一个存已用服务器(返回最小结束时间),一个存未用服务器(返回id最小),然后模拟一下过程即可
D:题目可以变成一段负一段正,然后尽量合并这些区间,其中中间一段正的贡献是2,最后一段正贡献为1,所以可以做两次,一次选了最后一段,一次不选最后一段,模拟的的过程用优先队列即可
E:用dfs跑,记录下当前处于深度多少,儿子几个,然后类似树那样模拟跑即可
F:枚举位数,然后算出这个位数一共多少个,这样就可以得知答案为几位数,用同样的方法就可以一位一位地推算出答案是啥了。现在想一下怎么计算x位数一共有多少个,首先肯定不能有前导0,那么我们可以手动将最高为设置成一个数,然后算x-1位答案有多少。问题就变成了,16种数,每个数有 a i a_i ai个,问在允许有前导0的情况下有多少种数字。 d p [ i ] [ j ] dp[i][j] dp[i][j]表示用了i种数,组成j位的方案数,那么转移方程就是 d p [ i ] [ j ] + = d p [ i − 1 ] [ j − k ] ∗ c [ j ] [ k ] dp[i][j]+=dp[i-1][j-k]*c[j][k] dp[i][j]+=dp[i1][jk]c[j][k]意思就是在i-1个数字组成了 j − k j-k jk位,第i种数字组成 k k k位。因为最终答案位数其实很小,所以不需要关心复杂度问题

---------------------------------------------------------------------------

场次:#388 (Div. 2)

C:用队列模拟过程即可
D:删除无效人后,先找到最大的两个人,然后用第二大的那个人的价格二分找最大的人的价格
E:ORZ
数学渣真的写不来概率题,复读下大佬的写法:
对于一对 ( a i , a j ) (a_i,a_j) (ai,aj)(i<j)
首先考虑他们在同一区间,这样的区间有 i ∗ ( n − j + 1 ) i∗(n−j+1) i(nj+1)个,而他们成为逆序对的概率为 1 2 \frac{1}{2} 21,而总区间有 n ∗ ( n + 1 ) 2 \frac{n*(n+1)}{2} 2n(n+1)个,所以贡献为 i ∗ ( n − j + 1 ) n ∗ ( n + 1 ) \frac{i*(n-j+1)}{n*(n+1)} n(n+1)i(nj+1),这里由于每一对都对答案有贡献,所以这部分的贡献 ∑ i = 1 n ∑ j = i + 1 n i ∗ ( n − j + 1 ) n ∗ ( n + 1 ) \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\frac{i*(n-j+1)}{n*(n+1)} i=1nj=i+1nn(n+1)i(nj+1),这个 O ( n ) O(n) O(n)就能算出来
接下来考虑他们不在同一区间,这样的区间有 n ∗ ( n + 1 ) 2 − i ∗ ( n − j + 1 ) \frac{n*(n+1)}{2}-i∗(n−j+1) 2n(n+1)i(nj+1)个,那么贡献为 ∑ i = 1 n ∑ j = i + 1 n n 2 + n − ( 2 ∗ i + 2 ∗ n ∗ i ) + 2 ∗ i ∗ j n ∗ ( n + 1 ) \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\frac{n^2+n-(2*i+2*n*i)+2*i*j}{n*(n+1)} i=1nj=i+1nn(n+1)n2+n(2i+2ni)+2ij,可是因为不在同一区间,所以 a i &lt; a j a_i&lt;a_j ai<aj无贡献,所以上述式子只有当 a i &gt; a j a_i&gt;a_j ai>aj时才有贡献,这个可以用树状数组求逆序对的方法就能求出来,假设到了i的时候逆序对个数为x,则式子变成了 ∑ i = 1 n x ∗ ( n 2 + n − ( 2 ∗ i + 2 ∗ n ∗ i ) ) + ∑ j = i + 1 n 2 ∗ i ∗ j n ∗ ( n + 1 ) \sum\limits_{i=1}^{n}\frac{x*(n^2+n-(2*i+2*n*i))+\sum\limits_{j=i+1}^{n}2*i*j}{n*(n+1)} i=1nn(n+1)x(n2+n(2i+2ni))+j=i+1n2ij,其中 ∑ j = i + 1 n 2 ∗ i ∗ j \sum\limits_{j=i+1}^{n}2*i*j j=i+1n2ij是只有当逆序对时才有贡献,这个也很好求,只要在将数字加入时候的权值设成j即可

---------------------------------------------------------------------------

场次:#389 (Div. 2)

C:如果走的是最短路,那么只会出现两个方向(左右选一个,上下选一个),否则必然选了另一个目标,这样统计有几段即可
D:对于一个串s,有两种情况。第一种为s不是回文串,那么只能寻找 r e v e r s e ( s ) reverse(s) reverse(s)来和s匹配,这种贪心即可。第二种为s自身为回文串,那么s既可以和自己匹配,也可以当作中心,这样需要分类讨论。有一个比较简单的处理,我们设定一个 m a x x maxx maxx d e b u f f debuff debuff,maxx表示只能当作中心串的那些串中的最大值,debuff表示选择了两个相同的,自身为回文的串,并且这两个串价值和为正数且其中一个串价值为负。假设我们在贪心的过程中不断更新答案(记为ans),这样最终答案就是 m a x ( a n s + m a x x , a n s − d e b u f f ) max(ans+maxx,ans-debuff) max(ans+maxx,ansdebuff),ans+maxx意思是我们新加入了一个自身为回文的串当作中心,ans-debuff表示我们删去一个自身为回文且价值为负的串
E:二分答案,记录每个数字出现次数,然后从最大数向二分的值扫,扫到x时,如果x/2小于二分的值,证明x已经不能分了,就把x出现次数加入到答案中,否则就模拟地将x分开,这样我们能保证分出的部分可能地多,也就是最优了
F:答案只有一个,为加权后的重心。这样求出重心以后,遍历所有节点,前k个放左边,后k个放右边,那么就能保证不会有同一子树中的组合了(因为重心的原因,所以最大子树保证小于等于k)

---------------------------------------------------------------------------

场次:390 (Div. 2)

C:开一个数组表示当前点不可能是哪些名字,对于每一句都这样处理完后,可以用dp来寻找答案, d p [ i ] [ j ] dp[i][j] dp[i][j]表示第i句为j说的时,上一个人是谁,这样dp完成后,就能反着去寻找答案了
D:题目相当于求线段的k次交,将每个线段按左端点排序后,开一个优先队列保存已选线段的右端,那么当选择一个新的线段时,就要从已选的线段中找右端点前k大的线段,这个用优先队列很好保存,那么一边遍历一边更新答案即可。输出方案可以通过记录最大答案时的位置,再遍历一次线段即可
E:纯暴力地更新,复杂度是 O ( n ∗ m ∗ r ∗ c ) O(n*m*r*c) O(nmrc),但是如果将比较的这个过程利用bitset优化,那么复杂度就变成了 O ( n ∗ m ∗ r ∗ c / 32 ) O(n*m*r*c/32) O(nmrc/32)这样的复杂度刚刚好能够接受,具体做法就是,开一个bitset< N > a[N][26]的数组,a[i][j][k]意味着i行,字母为j是否存在,那么对于另一个矩阵,如果是?则直接跳过,否则就比较目标行bitset且的值。不太会描述,总之就是利用bitset优化一行的比较

---------------------------------------------------------------------------

场次:#391 (Div. 1 + Div. 2)

C:读了半天才读懂,意思就是定义一个转换,使得转换后,任意集合所包含元素不变。我们能发现,只有当两个数字所处的集合完全相同时才能转换。这样统计一下所处集合相同的数字个数,然后乘以个数的阶乘就行了。
D:首先能想到,数字最多也才20个,那么明显可以状压,dp[i][j]表示最后一条线画在i后面且j为状态时的情况,那么答案就是 ∑ i = 1 n d p [ i ] [ j ] \sum_{i=1}^{n}dp[i][j] i=1ndp[i][j]其中j为1,3,7…不过这个题有一些坑点,就是100000000001这种情况,这样能够移动的位数不止5步了,同时如果你是向前枚举位数,要注意会爆掉int
E:
F:首先跑个最短路,然后思考下,虽然题目是双向边,但是对最短路产生影响的只可能一个方向,这样利用dist之间的关系,将图转化为DAG,然后就构造一颗支配树,dfs一次得出最大子树即可

---------------------------------------------------------------------------

场次:392 (Div. 2)

C:模拟题,可以将1到n再到2看成一个循环,这样好判断一些,然后就是一些细节判断了,没啥好说的
D:很明显位数越少越好,所以从后向前贪心即可,注意判断下0的情况即可。为什么是从后向前呢,考虑两种情况,第一种从前向后和从后向前位数相同,那么因为最左端那一块必定是从前向后的大于等于从后向前的,所以这种情况从后向前更优。第二种,有没有可能从前向后位数更少,答案是不可能,考虑最右端那一块,从后向前只可能大于等于从前向后的,那么剩下的部分只需要保证和从前向后一致就能保证位数一样了,综上所述,从后向前更优
E:一开始我们让每个边都尽可能地变小,自低向上来处理,这样就能够判断是否合法。然后我们从上到下,给每个边增加权值,这样做能够保证我们的每个修改都是合法的,并且能够保证尽可能增加了价值,也就是让答案尽可能地变大了
F:设等比数列的公比为 p q \frac{p}{q} qp,根据题意,p,q不同时为1,且互质,那么第n项比第1项为 p n − 1 q n − 1 \frac{p^{n-1}}{q^{n-1}} qn1pn1,所以第n项必须能够整除 q n − 1 q^{n-1} qn1 2 24 &gt; 1 e 7 2^{24}&gt;1e7 224>1e7,所以当 n &gt; 24 n&gt;24 n>24时无解,为什么等于24有解呢,因为等比数列为 a 0 , a 0 ∗ q . . . a_0,a_0*q... a0,a0q...第一项是没有乘q的
当n为1,2时答案可以直接得出,现在考虑 24 ≥ n ≥ 3 24\geq n\geq3 24n3的部分。因为最后一项要能够整除 q n − 1 q^{n-1} qn1,即 q n − 1 ≤ r q^{n-1}\leq r qn1r,当n=3,r=1e7时取到最大值,为3162,这样就能直接暴力枚举p,q了,(由于这个上限和n有关,n大了上限小,会发现并不会爆long long)当 l ∗ q n − 1 p n − 1 ≤ r l*\frac{q^{n-1}}{p^{n-1}}\leq r lpn1qn1r时有答案,为 r ∗ p n − 1 q n − 1 p n − 1 − l − 1 p n − 1 \frac{\frac{r*p^{n-1}}{q^{n-1}}}{p^{n-1}}-\frac{l-1}{p^{n-1}} pn1qn1rpn1pn1l1,这个式子的意思就是根据r算出最大的第一项的位置,然后算出最多几个第一项
(算上限的时候要手动加1,因为可能会炸精度)

---------------------------------------------------------------------------

场次:393 (Div. 2)

C:题目相当于一个有向图,答案就是环数+1个数是否为1,且环数为1时不记入答案
D:简单的dp,dp[i]表示到第i站,至少花多少钱,那么就只有三种转移,一种是前一个,一个是前90分钟以内,一种是前1440分钟以内,这个用lower_bound就能求出
E:如果把pop看成-1,push看成1,那么栈顶就是从右向左累加的第一个正数的位数。从右向左累加,也就是说,每一个操作都可以看作是[1,pos]上产生贡献,那么对于一个push,就是让[1,pos]加1,pop同理为-1,这个用线段数很好实现。现在要求出最右边的第一个正数,用线段数保存一个最大值即可
F:%q神的代码
如果我们将相邻的一堆点视作一个点(成为压缩),能够发现,不管怎么变化,变换后的串压缩后均为原串的子序列。那么如果我们能够求出每个长度的本质不同的子序列有多少个(本质不同的意思就是压缩后不同),就能统计答案了,统计方法就是:假设长度为l的串有x个,那么对答案的贡献就是 ( n − 1 l − 1 ) \binom{n-1}{l-1} (l1n1)(意思就是将原序列涂成l段,有多少种涂法)
现在想想怎么算本质不同的子序列有多少种设dp[i][j]为到了第i位,有了j个字符,转移很好写: d p [ i ] [ j ] = ∑ i = 0 n d p [ i − 1 ] [ j − 1 ] dp[i][j]=\sum\limits_{i=0}^{n}dp[i-1][j-1] dp[i][j]=i=0ndp[i1][j1]但是,这样转移会多算,例如abcb中,当i=4时,bb会算作是长度为2的子序列,但是实际上并不正确。一种处理方法就是多开一维,表示最后的字符是什么,字符相同则不能转移,复杂度 O ( 26 ∗ n 2 ) O(26*n^2) O(26n2),还有一种处理方法就是q神的代码那样处理,转移的理由就是:假如之前这个位置的j-1个字符最后一位是当前字符,那自然不能转移,如果不包括,这个字符串的贡献已经在之前算过了,也不能转移,复杂度 O ( n 2 ) O(n^2) O(n2)

---------------------------------------------------------------------------

场次:394 (Div. 2)

C:dfs的时候记录下最小值,剪剪枝就行了
D: b i b_i bi = a i a_i ai + c i c_i ci求出b序列,如果b序列在l,r的给定范围内,则符合要求,如果越界,则不符合要求。可求出b的最大值最小值,与l,r比较关系即可
E:如果有点度数大于4则无解。dfs构造即可,记录从哪个方向来的,当前点坐标以及延伸距离,我们将延伸距离初始设置成1e9,然后每向外建立一个点,就将这个值除以2,这样就能保证会有有相交了
F:首先,答案的矩阵一定是k个矩阵中的某一个,因为答案的矩阵也是一个special矩阵。
假设是矩阵x,涂色颜色是y。如果我们知道了其他矩阵到原图的距离和,以及其他矩阵对y的距离和,那么答案就能很好的计算了。计算方法是:其他矩阵在x涂色的区域的距离和,就是在计算其他矩阵在这个区域变成y的距离和,在x涂色意外的地方的距离和,就是计算其他矩阵到原矩阵的距离和。
现在想一下怎么计算这两个数组,首先g[i][j][ch]表示i,j这个位置颜色为ch的个数,利用二维差分可以计算出来(就是 g [ i ] [ j ] [ c h ] = g [ i − 1 ] [ j ] [ c h ] + g [ i ] [ j − 1 ] [ c h ] − g [ i − 1 ] [ j − 1 ] [ c h ] g[i][j][ch]=g[i-1][j][ch]+g[i][j-1][ch]-g[i-1][j-1][ch] g[i][j][ch]=g[i1][j][ch]+g[i][j1][ch]g[i1][j1][ch]),有了这个值,我们就能算出这个位置变成x的距离以及变成原图的距离了,然后将这个数组也二维差分一下,就能得到上面需要的数组了

---------------------------------------------------------------------------

场次:395 (Div. 2)

C:麻烦的写法是,dfs确认子树是否为一个颜色,同时计算出每个颜色的次数,这样便可以判某个点是否合法。简单的写法是,如果一条边连接的是颜色不同的点,那么这个点必须分割,这样统计出需要分割的边数的总数,然后判断下每个点所连的需要分割的边数是否和总数相等
D:根据四色原则,所以必定有解,因为变长均为奇数,所以左下坐标为奇数奇数的,必定都无法相邻。同理可以发现,左下角坐标奇偶性质相等的均无法相邻。所以四种情况都分别代表一种颜色即可
E:随意在数组中取两个数,设为 a , b a,b a,b,且位于等差数列的第 i i i项和第 i + k i+k i+k项,那么 b − a = k d b-a=kd ba=kd(d为公差),那么这样的差应该出现了 n − k n-k nk次,通过map计数可以求出这个值,那么就得到了k,那么通过 b − a = k d b-a=kd ba=kd就可以得到 d d d,那么检查下数列是否合法即可
    但是需要考虑考虑这种情况:设k=3,n=5,m=7,那么(s,s+4d)这一对不应该计入答案,可是由于 s = ( s + ( 4 + 3 ) d ) s=(s+(4+3)d) s=(s+(4+3)d)% m m m,所以(s,s+4d)会算入出现次数之中,这是错误的。什么时候会出现这种错误呢? ( n − 1 + k ) (n-1+k) (n1+k)% m m m== 0 0 0时会发生错误,由于题目保证了 n ≤ m n \leq m nm,那么最差时就是 2 ∗ ( n − 1 ) = = m 2*(n-1)==m 2(n1)==m,也就是说只有当 2 ∗ ( n − 1 ) ≥ m 2*(n-1) \geq m 2(n1)m才会发生错误。其他情况用上述方法即可。
    因为 g c d ( d , m ) = 1 gcd(d,m)=1 gcd(d,m)=1,所以数组关于{0,1,2,3,…,m-1}的补集所求出的公差和原数组应该是一样的,那么让首项加 ( m − n ) ∗ d (m-n)*d (mn)d即可

---------------------------------------------------------------------------

场次:396 (Div. 2)

C:dp[i]表示到i的分隔方法,f[i]表示分隔出的最小的子串数,dp[i]+=dp[i-j] f[i]=min(f[i],f[i-j]+1);
D:很明显的并查集,用i+n表示i的反意词,然后检验x和y的反义词、y和x的反义词是否在一个集合中,其实也可以看作一个加权并查集
E:按位算贡献,dp[x][0\1]表示x节点下,这位是0\1的个数,然后就可以和树分治一样,遍历一颗子树,算一下贡献,然后把子树加入到父亲当中

---------------------------------------------------------------------------

场次:397 (Div. 1 + Div. 2)

C:关键是要读懂题,必须是能够凑出完整对局,那么当一方盘数恰好为k的倍数,而另一方小于k则无解,其他情况均有解
D: g ( h ( x ) )   =   x g(h(x)) = x g(h(x))=x h ( g ( x ) )   =   f ( x ) h(g(x)) = f(x) h(g(x))=f(x)可得 g ( f ( x ) ) = g ( x ) g(f(x))=g(x) g(f(x))=g(x),所以根据f(x)可以得出一些g的取值相同,那么这个可以用并查集维护(其实因为 h ( g ( x ) )   =   f ( x ) h(g(x)) = f(x) h(g(x))=f(x),所以f(x)=f(f(x)) ),维护好了给在同一个集合中的g赋同样的值即可,从1开始赋值,这样赋值的最大值就是m了,到了这一步,我们保证了g数组是符合题意的。
然后通过 h ( g ( x ) )   =   f ( x ) h(g(x)) = f(x) h(g(x))=f(x) g ( h ( x ) )   =   x g(h(x)) = x g(h(x))=x,给h数组赋值,在给h赋值的过程中,如果发现不合法,则输出-1。最后输出答案的时候如果有 h i h_i hi=0那么将 h i h_i hi赋值为1即可
E:如果我们知道了根,那么就很好求了,暴力地每个点开一个set,然后合并即可。那么现在要想怎么求根,一种猜测就是根是直径的中点,也确实是正确的,因为如果产生必须合并一长一短且上面还有点的情况时,只有当这个点是直径的一半的时候合并成功。当然,我们也可以先随意dfs一次,找到任意一个非法的点作为根,这样的根也是对的
F:这题好多写法,但是我一个都想不到ORZ,官方题解的思路:
先离线,将每个询问根据 r r r存起来,然后从左向右扫数组,一点一点算当前值的贡献,对于当前位置 x x x,我们要求出每个询问右端点为 x x x的答案。考虑下怎么求,我们先只关心这样的情况 a j ≥ a i , j &lt; i a_j\geq a_i,j&lt;i ajai,j<i的情况(这样做完我们反着做一次那么答案就肯定对了),我们在权值线段树中找最近的这样的数,假设位置为 j j j,值为 Y Y Y,那么 1 ≤ l ≤ j 1\leq l \leq j 1lj的答案都可以更新为 Y − X Y-X YX,我们就这样找下去,相当于找到了前面所有大于 X X X的值,答案就都能知道了。可是这么写复杂度有问题,我们思考一下,发现只有当下一个发现的数字(假设位置为 j ‘ j^` j,价值为 Y ‘ Y^` Y)满足 Y − Y ‘ &lt; X − Y Y-Y^`&lt;X-Y YY<XY时候才有意义,因为不满足这个等式找到的解也是不如之前优的(不懂画画图应该就明白了),那么这样复杂度就变成了 O ( n l o g n l o g 1 e 9 ) O(nlognlog1e9) O(nlognlog1e9),就可以接受了
官方题解为主席树,这样按照上面的想法写就行了,每个点维护最右出现的位置。不过好多人写的是用归并代替了主席树,这么写会使得单次查询复杂度上限变为 n l o g n ∗ l o g l o g n \frac{n}{logn}*loglogn lognnloglogn大概算了下,是 l o g n logn logn的4倍,也不容易达到上限,可以接受,同时写法有一些变化,主要在找之前比他大的地方,看代码更好理解

G:如果你读到了这行字,请评论提醒我,我忘了写了

---------------------------------------------------------------------------

场次:398 (Div. 2)

C:处理出子树大小,然后贪心地去扫,如果一个子树大小符合,那么就假设这个子树被切开了,将结果反映到父亲节点上,最后如果符合条件数大于等于2就是有解的
D:二分买的个数,在优先买保质期长的情况下判断结果
E:我们注意到,价格都可以先%100,因为其他位没有意义。当我们今天零钱不够了,并且前面的某一天我们是用零钱支付的,那么我们就可以让那一天找钱,从而使得今天的零钱足够(同时那一天和今天的怒气也改变了)。我们发现,这种改变方式,会使得手上的零钱多100块,那么我们就可以开一个优先队列贪心了,每次都取店员怒气最小的即可。

---------------------------------------------------------------------------

场次:399 (Div. 1+Div. 2)

C:统计每个数出现次数,这样模拟k次,每次根据之前有多少个数,自己有多少个数,就能知道i和 i x i^x ix有多少个数字了
D:设dp[i][j]为到第i天,有j种物品的概率,那么 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ j k + d p [ i − 1 ] [ j − 1 ] ∗ n − j + 1 n dp[i][j]=dp[i-1][j]*\frac{j}{k}+dp[i-1][j-1]*\frac{n-j+1}{n} dp[i][j]=dp[i1][j]kj+dp[i1][j1]nnj+1,意思就是这次物品是不是新的,这样预处理完,然后每次寻找答案就行
不过有个问题,那就是第一维需要算多少次,根据题意,要保证物品数1000时也能至少达到0.5,然后打标得知打表到7300就能保证答案大于0.5,所以复杂度是O(7300*n),大概7e6
E:算出每个数字最多被几次取走,会发现用这个数字做原本的威佐夫博弈即可。
F:

---------------------------------------------------------------------------

场次:400 (Div. 1+Div. 2)

C:因为和最大1e14,所以符合题意的区间和最多 log ⁡ 2 1 0 14 \log_2{10^{14}} log21014个,把1和-1特判了,这样用个map存下前缀和,暴力判就行了
D:因为每个只有两个约束条件,所以当一个门锁着的时候,两个约束条件只能选一个,门开着,选两个或者都不选。那么一个开关选不选,是和另一个开关有着约束条件的。如果某个开关为x,我们假设x为选x,x+m为不选。假设某个门涉及的开关为u,v,那么如果们锁着,我们 l i n k ( u , v + m ) link(u,v+m) link(u,v+m)(意思是出现了u,那么必须出现v+m) l i n k ( v , u + m ) link(v,u+m) link(v,u+m),们开着 l i n k ( u , v ) , l i n k ( u + m , v + m ) link(u,v),link(u+m,v+m) link(u,v),link(u+m,v+m)这样就形成了约束条件。如果约束条件有冲突,那么就会导致 g e t f a ( u ) = = g e t f a ( u + m ) getfa(u)==getfa(u+m) getfa(u)==getfa(u+m)(意思是在某些约束中选了u,有的约束中选了u+m,导致出现了冲突)这样就能判断可不可以了
E:这题诈一看很唬人,仔细一看很简单
首先这个 f ( n ) f(n) f(n)就是 ϕ ( n ) \phi(n) ϕ(n),也很好证明, ( x , y ) = 1 (x,y)=1 (x,y)=1-> ( x , n − x ) = 1 (x,n-x)=1 (x,nx)=1-> ( x , n ) = 1 (x,n)=1 (x,n)=1,就是更相减损术那一套。
知道 f ( n ) f(n) f(n) ϕ ( n ) \phi(n) ϕ(n)后,根据性质就能知道 g ( n ) = n g(n)=n g(n)=n
然后因为 ϕ ( n ) \phi(n) ϕ(n)降的很快,所以接下来的完全暴力就行了

---------------------------------------------------------------------------

场次:401 (Div. 2)

C:预处理出某行到最多走到哪里,比如a最多走到b,那么a-b之间的行都最多走到b,然后更新数组,然后询问的时候判一下就行了
D:从上到下可能需要改回去,从下到上不需要。能发现这么写贪心,策略是唯一的,就很好写
E:比起最简单的那种单调栈贪心,这里就变成了二维的,能发现按照b是第一关键字,a是第二关键字,和最简单的那种贪心

---------------------------------------------------------------------------

场次:402 (Div. 2)

C:按价格差排序后贪心
D:二分答案,判下是否合法即可
E:模拟题,枚举每一位为1和0时的情况,记录答案就行
F:网上的题解写着是启发式合并,然而代码写的全是类似线段树合并的方法,纯暴力地合并,当然这样写是对的,因为合并两棵树,复杂度来自于相同的节点的路径,这一定小于其中较小的那颗树,所以这样写就自动带有了启发式,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。而启发式合并的写法,就是将子树信息加入到最大树中,然后不断删除和添加,这样的复杂度也是 O ( n l o g n ) O(nlogn) O(nlogn)

---------------------------------------------------------------------------

场次:403 (Div. 2)

C:题目限制的很多了,所以dfs模拟过程就行了,颜色最大数为某个点边数+1的最大值,在给子节点染色时暴力选择颜色即可
D:如果有的队伍第一种名称相同,那么他们都别无选择的使用第二种,如果有个队伍的第一个名字被用了,那他只能用第二种,如果第二个被用了,那他只能用第一种,如果都没有被使用,那就使用第一种。这个题的坑就在,即使两个队伍的第一个名字没有出现重复,也是可能相互制约的,要考虑好优先级
E:这个题的关键就是要看懂那个向上取整,发现是个向上取整的话,就会发现,每个点就算跑两次都能把这个图跑完,那么只要DFS一次,记录下路径,然后分k次输出就好了
F:dp[0/1][x][i][j]表示第一步为0/1,能不能 2 x 2^x 2x步从i走到j。转移就是
d p [ 0 / 1 ] [ x ] [ i ] [ j ] = d p [ 0 / 1 ] [ i − 1 ] [ i ] [ k ] dp[0/1][x][i][j]=dp[0/1][i-1][i][k] dp[0/1][x][i][j]=dp[0/1][i1][i][k]& d p [ 1 / 0 ] [ i − 1 ] [ k ] [ j ] dp[1/0][i-1][k][j] dp[1/0][i1][k][j](只要有一个k满足那么就是1)
这个DP基于倍增的思想,这样复杂度是 O ( n 3 ∗ 60 ) O(n^3*60) O(n360)是不能接受的,能够发现,这个dp的值只有0,1两种状态,所以可以使用bitset优化,这样复杂度就是 O ( n 3 ∗ 60 ) 32 \frac{O(n^3*60)}{32} 32O(n360)就可以接受了。
然后寻找答案就和倍增一样了,从大到小贪心即可

---------------------------------------------------------------------------

场次:404 (Div. 2)

C:简单二分下就行了
D:从左到右扫,当发现一个’('时进行计算,就能做到不重不漏。计算时,假设前有a个(,后有b个),要算 C a − 1 0 ∗ C b 1 + C a − 1 1 ∗ C b 2 + . . . C^{0}_{a-1}*C^{1}_{b}+C^{1}_{a-1}*C^{2}_{b}+... Ca10Cb1+Ca11Cb2+... ∑ i = 0 m i n ( a − 1 , b − 1 ) C a − 1 i ∗ C b i \sum_{i=0}^{min(a-1,b-1)} C^{i}_{a-1}*C^{i}_{b} i=0min(a1,b1)Ca1iCbi,这个要用范德蒙恒等式化简,看了这个博主的博客才学会。。。
E:每次操作,我们需要知道 [l+1,r-1] 这些数之中,大于a[l],a[r]的数有几个,小于a[l],a[r]的数有几个。用分块去写,每个块维护的是这个块的元素排好序的样子,例如:1 7 5 6 8 3 2 4 9,维护的就是:[1 5 7][3 6 8][2 4 9]。这样维护的意义就是,当我们查询的时候,对于块之间的数字,每个块可以直接二分求出这个块有几个数字比它大,在查询完后,把a[l],a[r]所在的块的位置暴力修改即可,这样单次查询复杂度就是 O ( n ∗ l o g ( n ) + n ) O(\sqrt{n}*log(\sqrt{n})+\sqrt{n}) O(n log(n )+n )
(其实这题看到就一股主席树+树状数组的味道,可是不太会写,而且难调+容易mle,所以有缘的话再写一写这个方法吧)

---------------------------------------------------------------------------

场次:405 (Div. 2)

C:从左向右贪心,如果遇到YES,则直接放k个不同的字符,如果遇到NO,那么就寻找已经染色的字符,将它染在最左的空位上,这样就能保证下一个不受到影响了,如果没找到染色的,就将自己的位置,和自己的下一个位置染色
D:很明显的树形dp,num[x][i]表示,x子树中距离x为i的个数,因为一步可以走k格,所以可以表示在模k意义下的距离,step[x][i]表示,x子数中距离x为i的步数总和,那么转移也很好求了,设在x子树下的有一个y节点,那么
s=(j+l+1)%k==0?(j+l+1)/k:(j+l+1)/k+1;
a n s + = n u m [ x ] [ j ] ∗ s t e p [ y ] [ l ] + n u m [ y ] [ l ] ∗ s t e p [ x ] [ j ] + s ∗ n u m [ x ] [ j ] ∗ n u m [ y ] [ l ] ; ans+=num[x][j]*step[y][l]+num[y][l]*step[x][j]+s*num[x][j]*num[y][l]; ans+=num[x][j]step[y][l]+num[y][l]step[x][j]+snum[x][j]num[y][l];
也很好理解,因为是两两匹配的,所以一颗步数总和和另一棵子树数量的乘积就是贡献。在算完一颗子树的贡献后,就跟树分治一样将这颗子树的信息加入到父节点中
E:首先除了v,k以外的字符都没用,我们把它看作x。设v为0,k为1,x为2,dp[i][j][k]表示用前i个v,前j个k,前k个x,排在最前面需要的步骤数,假设之前字符最后一位是v,那么我们就不能用k转移,所以我们需要再多开一维,那么转移就是:(num[i][j]意思是i之前j字符有几个,pos[i][j]表示第i个j的坐标)
int m=min(dp[i][j][l][0],dp[i][j][l][1]);
dp[i+1][j][l][1]=min(dp[i+1][j][l][1],m+len(pos[i+1][0],i+1,j,l));
dp[i][j+1][l][0]=min(dp[i][j+1][l][0],dp[i][j][l][0]+len(pos[j+1][1],i,j+1,l));
dp[i][j][l+1][0]=min(dp[i][j][l+1][0],m+len(pos[l+1][2],i,j,l+1));
意思在i,j,l这个状态下,分别试着加一个v,k,x的效果。我们需要让移动结果合法的条件下,步数最少,现在考虑一下新的那一位的位置,首先因为是相邻交换,所以这个字符的位置至少已经在i+j+l+1这个位置了,也就是说我们把它放最后最优。那么我们就能得到len函数了
x-min(num[x][0],i)-min(num[x][1],j)-min(num[x][2],k),不难发现这样不管这个字符处于哪个位置都是正确的

---------------------------------------------------------------------------

场次:406 (Div. 2)

C:倒着BFS,dp[i][0/1]表示i这个位置的胜负状态,0为必胜,1为必败,那么dp[i][0]可以由dp[i+a[x]][1]转移得到,转移方法也比较简单,如果可以由必败态转移得到则必胜,如果所有转移均必胜则是必败(可以用一个数组记录一下),复杂度就是 O ( n ∗ ∑ i = 0 k a [ i ] ) O(n*\sum_{i=0}^{k}a[i]) O(ni=0ka[i])
D:线段树优化建图,对于点到区间,就是直接连接,对于区间到点,就是再建立一颗线段树,但是底层的点共用,第一颗是上向下建边,第二棵是下向上建边,建出图以后,跑一次最短路即可。
E:将数字倒着插到主席树中,然后对于每个k来算答案,假如现在在算i的答案,并且我们找到了now这个位置,现在要知道剩下区间的数字种类,如果大于i,那么就要寻找第i+1个种类的位置x(用主席树很好实现),然后不断将now提到x并且块数加一,这样就能找到i时需要的块数。说白了就是用主席数来加速寻找第i+1个种类数字的位置。一个区间的数字种类个数,可以通过建立下标的主席数来做(就是spoj dquery那个题),至于为什么要倒着插,是因为now要向后推
网上还有这样一种写法,因为答案显然非严格递减,那么类似分治那样,对于一对l,r,判断下答案是否相同,相同就让这个区间的答案都划分为一个数,否则就递归继续处理。诈一看似乎是 O ( n 2 ) O(n^2) O(n2),仔细一想发现,本题无法构造出卡掉这种写法的数据,我只能想到卡成 O ( n n ) O(n\sqrt{n}) O(nn )的数据,就是 n \sqrt{n} n 个1, n \sqrt{n} n 个2,… n \sqrt{n} n n \sqrt{n} n

---------------------------------------------------------------------------

场次:407 (Div. 2)

C:求出相邻数的差值的绝对值,然后用这些差值作出前缀和(分别为正负正负…和负正负正),然后扫一遍,一边扫一边保存前缀最小值,顺便更新答案
D:首先判断下0的情况。然后,题目相当于将m-2条边加一条重边,使得这些边连接的点的度数都是偶数,由于欧拉路径要求有两个点度数为奇数,所以沒加重边的边必定有共同的定点(否则会使得奇数度数点有4个),那么每个点的贡献为 ( 2 度 数 ) \tbinom{2}{度数} (2)(不考虑自环)。
考虑下自环,自环有两种贡献,1.自环和自环作为剩下的两个每加的重边,这个显然可以,贡献为 ( 2 自 环 数 ) \tbinom{2}{自环数} (2),2.自环和非自环边,贡献为自环数*(m-自环数)。
E:题目可以转化为,有价值为负为正的物品,可以无限取,问最少需要几个物品使得价值和为0。虽然给了1e6个物品,但是物品只有差值有效,所以最多2001个物品有效。要使得物品数最小,直接bfs即可,时间复杂度最差为4e6

---------------------------------------------------------------------------

场次:408 (Div. 2)

C:根据题意,假设选取了一个点x,那么花费就是,a[x],与x相邻的点价值+1,与a距离为2的点的价值+2,这个用set乱搞就行了
D:有些边是不能被删的,所以不能够dfs贪心地去处理。为了留下不能被删除的边,可以bfs,会发现这些边总是能够在被第二次访问前被选入,因为如果这个边被第二次访问,意味着这个边并不是必取。那么一边bfs一边贪心即可
E:dp[i][j][x][y]表示到了第i位,还能偷看j次,且左边的人还能看x个,右边还能看y个,那么转移很好想,就是判断下当前这题有没有人有正确答案,以及自己的状态是啥。但是空间复杂度是 O ( n 2 k 2 ) O(n^2k^2) O(n2k2),需要滚动数组来dp。时间复杂度也不对,为 O ( n 2 k 2 ) O(n^2k^2) O(n2k2),这里可以发现,p其实并不需要跑满,只需要跑 2 ∗ n k 2*\frac{n}{k} 2kn次即可,那么复杂度就是 O ( n 2 k ) O(n^2k) O(n2k),可以接受了
F:

---------------------------------------------------------------------------

场次:409 (Div. 2)

C:二分时间即可,注意上限能达到1e10
D:显然变连续三个点是最优的,那么求一下点到直线距离,因为三个点都能动,所以答案要除以2
E:如果我们已经构造出了a位,乘积为b,下一位为c,那么就需要满足 a x ≡ c ( m o d m ) ax\equiv c\pmod{m} axc(modm),那么 ( a , m ) ∣ c (a,m)|c (a,m)c,那么 ( a , m ) ∣ ( c , m ) (a,m)|(c,m) (a,m)(c,m) ,即,下一项的与m的gcd是前一项的倍数。
那么我们先处理出m的因子,将0-m-1没有被屏蔽存到他们与m的gcd中,因为只有倍数可以转移,那么我们就将因子之间存在倍数关系的建立有向边,这个是个DAG,点x的权值就是0-m-1中没有被屏蔽且与m的gcd是x的个数,那么这样dp一次,就可以得出答案了。过程可以记录路径然后利用exgcd去得出

---------------------------------------------------------------------------

场次:410 (Div. 2)

C:两奇数一次转换为两偶数,一奇一偶两次转换为两偶数,那么让所有数字变为偶数即可
D:一个序列中,每两个中取其中较大的一个,这样取出的和是符合条件的。所以以A为第一个元素,B为第二个元素的数组,排序后,每两个中取B较大的一个就是答案(可以记录不取谁,这样好写)
E:如果我们对于任意两个点都根据两点大小关系建立单向边,那么找出拓扑序就能知道答案了。考虑下怎么建边。开一个b数组, a i = j 则 b j = i a_i=j则b_j=i ai=jbj=i,那么就有这些边: ( i , b i ) (i,b_i) (i,bi) j ∈ [ 1 , a i − 1 ] , b j &gt; i , j ≠ i j∈[1,a_i−1],b_j &gt; i,j≠i j[1,ai1],bj>i,j̸=i,但是极端时边数会 O ( n 2 ) O(n^2) O(n2),考虑如何优化。可以用线段树+dfs,用dfs完成拓扑排序,线段树找符合条件的点,当一个点用过了,就把这个点删去(因为它只会在拖拓扑序中出现一次)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值