专项训练4

2025暑假-数据结构专题

hh,上一个专题总结第一个也是数据结构。

快写啊要不然整个题单都要鸽了

1. Closest Equals

link:https://www.luogu.com.cn/problem/CF522D

2. Fibonacci-ish II

link:https://www.luogu.com.cn/problem/CF633H

3. Sasha and Array

link:https://www.luogu.com.cn/problem/CF718C

和上一道题很像。考虑每个节点维护一个矩阵,矩阵确实是斐波那契第n项的矩阵,但经过修改会乘上对应的值(不影响转移),pushup是左右矩阵相加即可得出总和。修改相当于整段数列向后平移x个单位,也就是叶节点的矩阵*(base^x),最后输出根节点的Fn上的数即可。

4. New Year Tree

link:https://www.luogu.com.cn/problem/CF620E

专项训练1-状压dp专项-3和这道题一样,不再赘述了。

5. Lucky Array

link:https://www.luogu.com.cn/problem/CF121E

考虑线段树维护每个数和最小的比它大的幸运数的差值,线段树每个节点记录当前区间注意到题目里的这样一句话:保证所有数操作前后都不超过 1e4。经过暴搜可发现总幸运数个数至多有31个,

完蛋了

dp(msb)

1.【模板】动态dp

link:https://www.luogu.com.cn/problem/P4719

带修改的dp,考虑用复杂度较低的算法暴力修改。首先最大权独立集问题是一个非常简单的树形dp,考虑把这个dp以更简单的式子表现出来。因为修改只会更改从这个点到根这条路径上节点的 DP 值,我们想到树链剖分,把轻重儿子分开简化复杂度。(每个点到根的路径上,最多经过 logn 条轻边。)具体的,设g[u][0]表示u的轻儿子可取或不取且加上u本身的最大权,g[u][1] 表示u的轻儿子都不取的最大权,那么能得出

批注 2025-08-08 174150
其中j是u的重儿子。
但是我们需要快速计算查询dp值,所以要将dp式子转化为另一种形式---矩阵乘法,但因为这里是max,就要用到一个有趣的trick:广义矩阵乘法,就相当于重新定义了矩阵乘法来支持dp转移。然后再套一棵线段树就能解决问题了。具体矩阵转移自己去翻题解吧。
最后是修改。修改就是不断跳重链,维护链顶轻儿子的转移矩阵。但不得不说这种修改我还是第一次见,简单暴力啊。

点击查看代码
void modify(int u,int w)
{
	num[u].f[1][0]+=w-a[u];
	a[u]=w;
	while(u!=0)
	{
		mat x, y;
		x=query(1, dfn[top[u]], ed[top[u]]);
		update(1, dfn[u]);
		y=query(1, dfn[top[u]], ed[top[u]]);
		u=f[top[u]];
		num[u].f[0][0]+=max(y.f[0][0], y.f[1][0])-max(x.f[0][0], x.f[1][0]);
		num[u].f[0][1]=num[u].f[0][0];
		num[u].f[1][0]+=y.f[0][0]-x.f[0][0];
	}
}

2. 烦人的数学作业

link:https://www.luogu.com.cn/problem/P4999

数位dp(我疑似学过?但跟没学差不多),也就是把数拆位考虑。设dp[i][j]表示填到第i位,前i-1位答案为j的总答案枚举0~9填即可。再维护一个lst记录限制,当这一位以后的所有位都达到最大值时lst为1,那么当前位最大只能填到该数这一位的最大值。记搜转移查分查询。

线段树(msb)

其实并没有写什么,因为去学主席树了,那我还是放一道上上题单要写的题吧。

1. Escape Through Leaf

link:https://www.luogu.com.cn/problem/CF932F

拖了这么久才写是因为不会李超线段树TT所以说这段时间真的学了点知识(这里用点是因为我只打板子)。

首先设dp[u]表示从u到叶节点的最小代价,推出一个简洁的dp式子:\(dp[u]=min(dp[v]+a[u]*b[v])\)(v是u的儿子),然后将b[v]看成斜率,dp[v]看成截距,那么问题转化为在平面上有一些直线, 选出与直线x=a[u]相交的最靠下的点。拿李超线段树求就可以了。但因为dp转移时要从多个儿子转移过来,相当于需要在一棵融合了 所有儿子的李超线段树 的李超线段树里查询,那不就是李超线段树合并么,和普通线段树合并的写法几乎没有区别。

数论(msb)

其实也没有写什么,那我还是放一道以前没改的题吧。

1. [六省联考 2017] 相逢是问候

link:https://www.luogu.com.cn/problem/P3747

来自于2024年暑假CSP-S24,当时并没有改出来(但最终交了一份拼合了暴力和正解的代码),原因不详。
然后因为今天要改这道题又重学了一遍欧拉定理,感觉是第4次开始学它了,但我依然没有看懂扩展欧拉定理(第3类)的证明,没关系。

这道题吧,首先要观察到一点是(对不起,不会打Latex):

批注 2025-08-12 174918

所以考虑预处理出每个数在log p次操作下会变成多少,然后用一种神奇的手段快速在区间中找出哪些数的操作次数<log p,然后就变成单点修改和区间查询啦。
这个神奇的手段是什么呢?给每个数开个数组tim记录操作次数,再开一个数组nxt记录下标≥i的第一个
操作次数<log p 的数的位置,然后用一种类似于并查集的思想把它连起来,就长这样:

点击查看代码
int get(int i)//查询下标大于等于i的第一个操作次数<logp的数 
{
	if(tim[i]<logp) return i;
	else return nxt[i]=get(nxt[i]);
}

然后剩下的就不难理解了。但代码还是有点难写。

图论

同余最短路:就是一个长成\(a[1]x[1]+a[2]x[2]+...+a[n]x[n]=B\)的式子,B有上限要求,给定\(a\)数组等,求B的个数。

考虑钦定一个数,比如\(a[1]\),设 \(dis[i]\) 表示 \(mod a[1]=i\) 的最小的数,将 \(i\)(0~a[1]-1) 和 $ (i+a[j])mod a[1]$j(2~n),然后

贪心

1. Yet Another MEX Problem

link:https://www.luogu.com.cn/problem/CF2146E

应该得是自己能想出来的,但因为真的太困了,最基本的性质没有想到。感觉我自己的思路跟题解完全是反着的(?)
首先求区间大于mex的数的个数这一点,我们可以考虑去枚举这个mex,如果b数组有和mex相同的那这个mex对这个区间的贡献为0,其他的mex的贡献取max就是答案。因为mex是要求最小的那个,所以其他mex一定不会优于真正的mex。那么我们可以考虑维护不同的mex对当前区间的贡献。
考虑从前往后扫一遍,每加进来一个a[i],那么所有比它小的mex的贡献都可+1,等于它的都变成了0,剩下的保持不变。所有mex里贡献最大的即为答案。用线段树维护。
这种题的套路还是只用考虑怎么从上一位转移到这一位,想简单一点。

2. Make Good

link:https://www.luogu.com.cn/problem/CF2143E

脑电波题不可做。
有一句很重要的话就是:

遇到这种,操作无限次的题目,一个非常常见且好用的切入点是,观察操作中的不变量。

然后观察这个修改操作,注意到他每次都是连着修改两个,且修改前后的左括号数量奇偶性不变,考虑统计奇位和偶位各自的左括号数量(字符串下标从0开始),因为是同加同减,所以它们的差值是固定的。

然后我直接放题解好吧

批注 2025-10-23 161006

3. Long Journey

link:https://www.luogu.com.cn/problem/CF2145F

Q:这个题和贪心有什么关系吗?
A:有,因为只要能走就一定会走一步。

注意到m很大,所以它一定是周期性的,大概率会是矩乘和倍增一类的东西,虽然倍增容易被卡常但是好写很多,所以学习的是题解的倍增做法。倍增套路是什么?dp[i][j]表示当前在i的位置,往后2 ^j的单位的值/步数。那么迁移到这个题上,设dp[i][j][k]表示当前在第i回合,在j的位置,往后2 ^k个回合后的最大步数(为什么这里选择倍增回合而不是位置?因为回合是不断往前走的,它不会暂停,而位置会因为激活情况而停下)。观察到题面里的%a[i]操作(设所有a[i]的lcm为lcm)。不难想到每lcm个格子的激活情况是相同的,lcm最大为2520,那么x的范围是10,j的范围是2520,k是45,所以这个复杂度是正确的。
然后考虑初始化,显然就是考虑k=0的情况,也就是从每一个回合,每一个位置判断下一位是否可以走。
转移就是倍增转移:dp[i][j][k]=dp[i][j][k-1]+dp[(i+(1ll<<(k-1)))%n][(j+dp[i][j][k-1])%llcm][k-1];
判断一下如果dp[0][0][44(最大的k)]如果还<m的话就是-1。
然后求最终答案就相当于树上求k级祖先一样,从大到小拼起来,但是需要注意的是判断有没有到m不能加等于号,因为有可能已经到了m了,但因为m+1到不了只能原地等待,这样会多算答案。
因为没有等于号了所以最后还要加一个1。

4. Monotone Subsequence

link:https://www.luogu.com.cn/problem/CF2152E

第一道交互题。交互题还是蛮有意思的啊。

首先因为你啥都不知道所以可以直接询问极端情况。比如对于这道题来说,直接询问[1,n+1]这个区间(称为询问集合)的情况(这里还是能想到的)。然后考虑接下来怎么办。会发现它回答的几个数一定是单调递增的,考虑将这几个数从询问集合里删掉。然后再继续输出询问集合,不断重复n次。这样子就能保证我们每次都能得到一个单调上升子序列,且这n个单调上升子序列的最大值是单调下降的。然后如果这个过程中有回答的数的个数>n,那么就直接输出完事儿了。否则n次下来一定会有一些数没有出现在前面的单调上升子序列中,而且这些数的数值一定不会大于第n个上升子序列的最大值。考虑将n个单调上升子序列的最大值取出来就构成n个单调下降的数,再将未出现的数中随便取一个输出就行了。

5. I Yearned For The Mines

link:https://www.luogu.com.cn/problem/CF2133E

还是很神奇的题啊,自己想的完全八竿子达不到正解啊。

首先会发现如果是一条链的情况,直接从链的一头查到另一头就行了。那么就考虑通过2操作将这棵树剖分成若干条链,然后这些链上的点需要O(n)的复杂度挨个检查,剩下n/4次操作2。

然后就有一个非常神奇的结论就是如果当前点的siz>=4,就把这个点操作2一下,从第一个角度理解它就会发现4是最小的 当前点的子树有可能不能构成一条链的 个数,从第二个角度理解就是n个点每四个划分一次。那么这样划分完就会形成若干条最长链长度不超过3的链。

代码实现就是正常dfs,记录要划分的点,标记要摧毁的边,然后从仅有1条连边或者没有连边的点开始跳(链头和孤点),记得多测要清空TT。

多校-dp

1. Multitest Generator

link:https://www.luogu.com.cn/problem/CF1798E

首先能观察到对于一个序列,操作数不会大于2的,因为只要改前两个数即可。(第一个数变成1,第二个数变成序列长度-2)。设f[i]表示i的后缀能形成的test的个数,那么f的转移不难相出。然后考虑答案怎么算,如果当前的a[i]==f[i+1]且f[i+1]!=0,那么就已经合法了,如果这两者都不满足,那么只需把a[i]改为f[i+1]就行,剩下的情况就是f[i+1]=0了,如果答案是1,那么就是要求将i+1的后缀只修改一个就能满足要求,设g[i]表示在i的后缀中修改1次能得到的最多test数,因为如果能通过1次修改就能得到x个test,那x-1个test,x-2个test...也是不难得到的,那么只要判断g[i+1]是否>=a[i],是那么答案为1,否答案为2。

g的转移也不是很难,自己推推看吧。

2. Bitwise Slides

link:https://www.luogu.com.cn/problem/CF2066C

神奇的dp,一点性质都没发现。

记1,2,3,4...i的异或和为s[i]。首先因为这n个数每一个都被异或过一遍,所以操作到最后三个数的异或和一定就是s[n],又因为有两个数是相同的,所以最后必定有一个数是s[n]。


世界灿烂盛大。欢迎回家。

posted @ 2025-08-12 18:17  zhouyiran2011  阅读(26)  评论(0)    收藏  举报