2024.10 做题记录 / 耽溺这虚假 无所谓结局代价
CF2004E
套用 SG 函数的结论,我们先打单个游戏的表再异或即可得到答案。首先对于一个大小为 \(i\) 的堆有 \(SG[i]=\text{mex}_{j\bot i}\{SG[i-j]\}\),容易暴力 dp。
int SG[N];
int f(int x) {
if(SG[x]!=-1) return SG[x];
if(x==0) return SG[0]=0;
vector<int> g;
up(i,1,x) if(__gcd(i,x)==1) g.pb(f(x-i));
sort(g.begin(),g.end());
if(g[0]>0) return 0;
up(i,0,(int)g.size()-2) if(g[i]+1!=g[i+1]&&g[i]!=g[i+1]) return g[i]+1;
return g[g.size()-1]+1;
}
首先 \(SG[0]=0,SG[1]=1\),然后第 \(i\) 个质数的 SG 值是 \(i\) 强相关的,合数的 SG 是最小质因子的 SG,线性筛求出即可。
CF2005E1/2
E1 的话直接按照博弈论的东西暴力 dp 即可,是 \(O(lnm)\) 的,具体而言对于一个位置考虑其子矩阵中代表这一轮的格子,如果不能到达必胜态就是必败,暴力 dp 即可,\(f[i][j][k]\) 表示 \((i,j)\) 往下的矩阵中第 \(k\) 轮的状态,注意定义是从 \((i,j)\) 走而不是走到 \((i,j)\),容易倒序 dp。
现在就要优化 dp 了,这个转移本身形式感觉没啥简便优化,找找题目性质,注意到如果一次转移可以走到多个点 \(x=p_1,\dots,p_x\),一定走一个右下方没有 \(x\) 的位置,因为如果走到下面都输那走上面一定会输但是走下面会赢的走上面不一定会赢(下一手选择被包含了),那有用的点按照横/纵坐标排序之后纵/横坐标会有单调性,这代表了按照横/纵坐标排序之后任意矩阵能覆盖到的合法点集是一个区间。那直接对每一轮记一下合法位置,对 \(f\) 做一个前缀和,查询的时候二分之后差分,判断一个子矩阵有没有胜态即可,复杂度 \(O(nm\log nm)\)。
CF2005D
经典结论:序列上固定一个左端点之后向右拓展右端点,本质不同的 \(\gcd\) 至多只有 \(\log\) 种。因为后面的 \(\gcd\) 一定是前面的 \(\gcd\) 的因数,每次变化至少使得值 \(\times\frac{1}{2}\)。
对于每一个点预处理出每一个作为左端点之后序列上的 \(\gcd\) 的段,一个段放在一起考虑,显然是拿最右边的不劣,因为左边的贡献一样的话,右边留下的数少更容易让后面部分的 \(\gcd\) 变大,设 \(L_{a/b}[i]\) 表示序列 \(a/b\) 第 \(i\) 个数作为左端点的 \(\gcd\) 段的右端点集合,预处理可以递推,求答案暴力枚举即可,复杂度控制在几只 \(\log\) 呢。
CF2002E
按照连续的颜色的段考虑,在删出空段前是频繁的每次去头,删除空段之后如果该段左右段颜色相同则会导致合并,感觉容易拿数据结构维护。
维护当前删除减量 \(\Delta\),用 stl set(不熟练 set 可以写带删除标记的 priority_queue)维护二元组 \((x,y)\) 表示段 \(x\) 的长度是 \(y+\Delta\),以 \(y\) 为关键字排序。每次找出最先被删空的那一段,向前推进时间,更新一下用 stl map 维护一个前后指针即可。
CF2003 E2
首先先考虑往序列里面投一个数会得到什么,设序列中第一/二个没有出现的数为 \(L,R\),则投机 \(L\) 得 \(R\),否则得到 \(L\)。
先形式化描述一下,首先 \(L\to L\) 这种当成没有发生就行了,所以是我们可以选择 \(?\to R\) 或者 \(L\to R\)。碍于两种移动相互限制,先想办法找点性质,感觉第一类操作这个凭空变出某个数只用一次够了,那只连 \(L\to R\) 的边,可以发现因为只有小往大所以是 DAG 这很方便,设 \(f_i\) 表示从 \(i\) 走能到达的最大的点是什么,按 topu 序倒序 dp 即可。具体而言度数 \(>1\) 的点和 \(x\) 可以作为根,然后任意 \(L\) 可以作为答案,然后就是求出从任意根开始走能走到的最大点。
AT_abc281_g
手玩之后发现按照 dis 分层之后,同层/相邻层 之间可以任意连边。不妨设 \(f[i][j]\) 表示 \(i\) 个点最后一层有 \(j\) 个点的方案数。注意到 \(1/n\) 有特殊性,\(1\) 的在初始化那里可以体现,那不妨先做 \(n-1\) 个点的这个 dp 再考虑加入 \(n\) 的贡献。
考虑从 \(f[i][j]\) 加一个一层 \(k\) 个转移到 \(f[i+k][k]\),乘法系数有选择编号的贡献 \(\binom{n-1-i}{k}\),异层连边的贡献 \((2^j-1)^k\),同层连边的贡献 \(2^{\frac{k(k-1)}{2}}\)。最后答案 \(Ans=\sum_{i=1}^{n-2}f[n-1][i]\times(2^i-1)\),做完了。
P9197
考虑按照 \(a\downarrow\) 的顺序插入,目前维护 \(\geq a_i\) 部分的贡献,未来贡献延算。
特判 \(n=1\),设 \(f[i][j][k][t]\) 表示考虑了最大的 \(i\) 个,目前有 \(j\) 段闭合的,贡献为 \(k\),以及 \(t\) 个端点被钦定的方案数,转移刷表是比较显然的,考虑 \(f[i][j][k][t]\) 可以转移到的位置,首先新的贡献一定是 \(d\gets k+(2j-t)\times (a_{i+1}-a_i)\) 呢(
-
当端点,系数为 \(2-t\),分成新开/不新开一段转移,\(\to f[i+1][j][d][t+1],f[i+1][j+1][d][t+1]\)
-
合并两段的点,系数为 \(j-1\),\(\to f[i+1][j-1][d][t]\)
-
放在一段的左右,系数为 \(2j-t\),\(\to f[i+1][j][d][t]\)
-
新开一段,系数为 \((j-1)+(2-t)\),\(\to f[i+1][j+1][d][t]\)
最后的答案是 \(Ans=\sum_{i=0}^{L} f[n][1][i][2]\),写的时候注意转移的越界问题。
P9870
运气好的话这是个简单题,但是前提是运气好。
不妨设 \(a\) 是小的序列,\(b\) 是大的序列,\(k\) 是 \(b_k\) 最大的位置,因为 \(k\) 这里是最好的限制了,所以可以用 \(a\) 的前缀做到 \(k\) + \(a\) 的后缀做到 \(k\) 这样子贪心拼出序列,此时我们可以让前/后尽量多匹配,这样不用考虑有剩的怎么办了。特别的,如果有多个 \(b_k\) 最大,考虑前缀的时候我们考虑到最大的 \(k\),考虑后缀的时候我们考虑最小的 \(k\),然后考虑以 \(i\) 为分界能不能覆盖满序列,特别的 \(i\) 可以同时出现在前缀和后缀中,因为最后会交。
现在考虑怎么求出一个 \(a\) 的前缀最长的匹配多少 \(b\) 的前缀,后缀做法一致不重复讲述。如果我们想让 \(a\) 受到的限制尽量宽松的话,不难想到按照单调栈分成小块,这个有每一块的结尾单调递增的性质,然后能走一块就尽量往前走,然后对于单点的答案肯定是在这一块的结尾或者下一块中且好求甚至可以不求,同时保持了贪心限制最宽松,做完了。
CF1437F
按照 \(a\uparrow\) 考虑,预处理 \(d_i\) 表示可以用小生效的极大前缀。设 \(f_i\) 表示以 \(a_i\) 为最大值,考虑到 \(d_i\) 的方案数,转移从 \(2a_j\leq a_i\) 的 \(f_j\) 考虑,系数要考虑 \(d_j+1,\dots,d_i\) 这部分,结合题目性质(\(j\leq d_i\) 以及之后任意放)易得系数为 \(\binom{n-d_j-2}{d_i-d_j-1}\times (d_i-d_j-1)!\),答案只能是 \(f_n\),如果不能覆盖也就是 \(d_n<n-1\) 那就无解。
AT_agc030_f
懒得写了,大概就是什么先去掉 B 的顺序贡献之后排序考虑,设 \(f[i][j][k]\) 表示考虑了 \([i,\dots,n]\) 还有 \(j/k\) 个出现/没出现过的点的方案数,转移是容易的。
P6383
注意到不能有相交链接,容易手玩发现这个一定不行,否则一定行,构造方案是容易的。具体而言,如果先动下/上面的链另一个肯定无法做到,构造合法方案只要把连通块顶端的被连点按深度 \(\uparrow\) 做一下就变成子问题了。
如果在链上做那就是设 \(f[x][i]\) 表示子链 \(x\) 可以往下选择 \(i\) 个位置的方案数,放到树上就必须要合并了,先考虑子树的话状态只能是表示 \(f[x][i]\) 表示子树 \(x\) 向上有 \(i\) 个位置转移的方案数,那考虑怎么转移,首先先把子树合并上来,显然是 \(f[x][i]=\prod f[y][i+1]\),这个用到的是显然 \(x\) 可以给 \(y\) 选别的能选性质一致。然后考虑 \(x\) 怎么贡献,往下的贡献应该是 \(f[x][i]\to f'[x][j](j\leq i)\),但是现在是向上贡献,所以反过来,给 \(f\) 做前缀和即可。注意初始化的时候叶子也是要做前缀和的。
CF1917E
有一个显著有用的构造:\(2\times 2\) 的小正方形全部放 \(1\),这样子全局没有影响,于是 \(4|k\) 的已经构造好了。
如果 \(k\) 是奇数肯定无解,现在想办法做一下 \(k\equiv 2\pmod 4\) 什么的 >w<
对于 \(k=2\),发现只有 \(n=2\) 时有解。注意 \(k=2\) 以及 \(k=n^2-2\) 其实是等价的问题,所以如果 \(k=n^2-2,n>2\) 其实也是无解的,没有直接写出这个还是比较影响做题的(
现在 \(k\geq 6\) 不妨先拿 \(k=6\) 看看怎么构造然后想着把剩下的 \(4\) 个一组塞进去,不难构造出如下 \(3\times 3\) 的矩阵,贪心放 \(4\) 之后外面一圈没有贡献不妨也画上去,然后发现能多凑一组画在右边。
1100 | 1100
1010 | 1010
0110 | 1111
0000 | 1001
右边矩阵亏空 \(0\) 的个数为 \(6\) 也就是能做到 \(k=n^2-6\) 了,这样就做完了 qwq
P1392
容易想到拿堆去微调贪心,先给每一行的 \(a\) 排序,每次考虑拿某一行更大的一位 \(a\),问题是怎么去重,不妨钦定一下种方案的产生方式并且保证父亲状态答案比该状态更小即可。假设当前序列在每一行的增量次数是 \(\{\Delta_1,\dots,\Delta_m\}\),钦定状态的产生方式是按照行顺序往上叠,换句话说父亲状态是找到最后一个 \(\Delta_i>0\) 使其减 \(1\),这样子换到搜索里面能拓展的状态就是比最后一次操作后的操作可取,这个操作的后的意思是 \(i\) 更大的更大,否则 \(\Delta_i\) 大的更大。
P7831
设 \(f_i\) 表示点 \(i\) 要无限走至少要多少的初始代价,对于边 \((x,y,p,v)\) 显然有转移 \(f[y]\gets\min\{\max(f[x]-p,v)\}\),为了使用这个式子我们需要找一些能计算出初始值的 \(f\)。
考虑一个环上面有没有初始值,注意到出边的 \(v\) 最大的那个点 \(f\) 至少为 \(v\) 且 \(v\) 一定合法,是完美的初始值。现在的问题变成了怎么判断一条边可不可以作为一个环上限制最大的点,这个有三种思路,单独考虑每一条边,使用双指针从小往大加边/从大往小删边,前两种经过思考感觉不可做/麻烦,而第三种就不一样的,非常的简便。
我们考虑在从大到小删边的过程中维护能到达强联通分量的边集,一开始就不能到达的就滚吧。然后要考虑删边带来的影响,删边可能会导致一些点没有出边,没有出边就废了,都有出边一定就可以(基环树),拓扑排序删边调整到有连边的点都有出边即可,最优答案的转移可以在这里顺便做掉,于是就做完了 >w<
P5298
设 \(f[i][j]\) 表示在点 \(i\) 权值为 \(j\) 的概率,叶子结点 \(f[i][j]=[j==v_i]\),只有一个儿子时 \(f[i]=f[son]\),对于有两个儿子的,设 \(i\) 的儿子为 \(l,r\),转移显然有 \(f[i][j]=f[l][i]\times[p_i\times\sum_{k=1}^{j-1}f[r][k]+(1-p_i)\times \sum_{k=j+1}^{tot}f[r][k]]+f[r][j]\times[p_i\times\sum_{k=1}^{j-1}f[l][k]+(1-p_i)\times\sum_{k=j+1}^{tot}f[l][k]]\),线段树合并优化即可。
P3827
看到【建议降蓝】进来做的。
显然只需要考虑质数,而 \(10^6\) 以内的数最多有 \(7\) 个质因子,所以带一个质因子的枚举是没有问题的。那把一个数摊成一个长度 \(\leq 7\) 的质因子序列(线性筛预处理),问题变为求区间有没有相等的数。
考虑 \(nxt_i\) 为 \(j>i\) 且 \(a_i=a_j\) 的 \(\min\{j\}\),可以用 \([\max_{i\in[l,r]}\{f_i\}\leq r]\) 来判断,这个容易用 set
+ 线段树维护。
P3449
最开始没看到回文,感觉是一个非常一眼的字典树+hash。但是其实回文有更简单的做法,设 \(h_i\) 为 \(s_i\) 的 hash 值,那么拼接 \((i,j)\) 合法的条件是 \(h_i\times base^{len_j}+h_j=h_j\times base^{len_i}+h_i\),可以得到 \(\frac{h_i}{base^{len_i-1}}=\frac{h_j}{base^{len_j-1}}\),这个新哈希相同的贡献即可。
AT_agc028_e
因为怕抄不懂题解所以还是要认真写一下题解。
字典序最小的话,从前往后填写答案,如果能填 \(0\) 则写,否则写 \(1\)。问题改变成了钦定一个前缀的选法之后,有没有一种后缀的划分可以符合题目条件。
有显然的基础观察:称原序列中的前缀 \(\max\) 元素为 Jelly Fish 元素,水母元素不管怎么分一定还是前缀 \(\max\) 一定会贡献。对于非水母元素,如果其在一个序列中贡献了,那么放在另一个序列一定不会贡献,因为血脉压制这个元素的元素一定在另一个序列。
这个两边单独串着单调栈感觉很不可以做,题目要求差为 \(0\) 的情况,那在这个上面找找条件帮助约束,发现如果可以的话,一定可以找到一种构造使得后面的前缀 \(\max\) 都是水母元素。因为对于一种构造,假设两边都存在贡献的非水母数,一定可以通过微调调没一边的,具体而言交换会产生贡献的段即可(题解这里说直接交换,但是感觉很怪,可能我理解能力有问题吧)。
不妨钦定只有 Jelly number 贡献的序列为 A,显然 B 同理。现在问题又被转换成了能不能使得 \(pre_A+Jelly_A=pre_B+Jelly_B+unJelly_B\),因为 \(Jelly_A+Jelly_B=k\),不妨写成 \(pre_A-pre_B+k=unJelly_B+2Jelly_B\),注意到 \(unJelly/Jelly_B\) 都可以去蹭下界,但是 \(unJelly\) 的贡献不好描述怎么蹭,\(JellyB\) 的贡献比较好蹭,把一个贡献连通块都放到 \(A\) 就行了,可以构造奇偶性相同的更小的,所以要这个的 rmq,将 Jelly number 给权 2 求后缀 \(\geq \alpha\) 的带全权 LIS 这样子,写线段树优化 dp 带个撤销就行了。
P4036
警示后人:因为区间可能是 \([1,0]\) 所以二分 seek 一个 position 的时候要特判 \(0\)。
比较一眼的线段树维护哈希,查询套一个二分即可。问题是怎么预处理 position,先上一个有脑 树状数组+ 二分 简化问题,发现离线下来时间倒流很好做,那就倒流预处理。
P10991
这个题目思路不是很难但是很别扭,非常的别扭,我极少在题解里面写“因为走投无路”。
\(p\leq q,A_q-A_p\uparrow\),直观但是感觉有问题的思路是 \(A_q\uparrow / A_p\downarrow\)。虽然这个直观上是有问题的思路,但是这个题目除了这个走投无路 & 想了至少能看看怎么贡献我们还是只能弄一下这个 >w<
如果想要 \(A_p\downarrow\),那么应该是想要 \([p,\dots]\),因为 \(p\) 往左拓展不会使得贡献更好。同理如果 \(A_q\uparrow\),那么应该想要 \([\dots,q]\),因为 \(q\) 往右拓展不会使得贡献更好。
因为实在走投无路所以可以猜测并发现考虑上面这两种情况可以取到最优答案,下面我们考虑一下证明 \([p,\alpha]\) 拓展到 \([p-1(\Delta),\alpha]\) 不会更优:如果被拓展的数 \(x\) 满足 \(x\leq Ans_q\) 那么显然不优秀,否则会被放到右边,那把原本位置在右边的删掉不劣,一直删除就变成了另一种情况。
然后维护的话用一个类似对顶堆的东西正反各做一遍即可。
P5537
假设一个行走序列存在,那么这个行走序列应该是存在的,可以用哈希判断,所以预处理出所有的哈希。
从 \(x\) 开始走 \([l,\alpha]\) 可以转成判断 \(x+[l,\alpha]\) 的哈希存不存在,哈希即可。因为哈希可能不存在,且存在的哈希应该是一个 \([l,r]\) 的前缀,可以用二分去草。