模拟赛总结
本文章统计了自2024/11/21开始的模拟赛总结
北中OJ模拟赛分数汇总
分数格式:实际得分/预计得分/以我的实力应该得的分数
| 题目 | 24.11.21 | 24.11.23 | 24.11.25 | 24.11.26 | 24.12.21 | 24.12.28 | 25.1.4 | 25.1.8 | 25.1.13 | 25.3.23 | 25.5.20| |
|---|---|---|---|---|---|---|---|---|---|---|---|
| A | 100/100/100 | 100/100/100 | 30/30/100 | 20/20/20 | 100/100/100 | 100/100/100 | 100/100/100 | 100/100/100 | 100/100 | 20/100/100 | 98/100/100 |
| B | 80/100/100 | 70/70/100 | 0/30/55 | 0/0/100 | 0/20/20 | 5/25/100 | 100/100/100 | 0/37/100 | 90/100/100 | 100/100/100 | 0/0/0 |
| C | 0/0/50 | 0/0/64 | 0/0/0 | 100/100/100 | 20/20/45 | 0/0/0 | 10/10/10 | 0/0/0 | 0/0/14 | 15/20/55 | 0/0/0 |
| D | 0/0/40 | 0/0/30 | 0/0/50 | 0/0/20 | 0/0/0 | 0/0/10 | 30/30/60 | 无 | 无 | 20/20/100 | 无 |
| 总分 | 180/200/290 | 170/170/264 | 30/60/215 | 120/120/240 | 120/140/165 | 105/125/210 | 140/140/170 | 100/137/200 | 190/200/214 | 155/240/355 | 98/100/100 |
模拟赛总结
24/11/21
策略:出现失误。前面慢吞吞写了很久T1、T2,摆了很久。后面两题没时间打暴力,没有拿到90pts。
T1
小模拟。多项式除法,直接做就做完了。推式子花了点时间,有点慢。
T2
数论;DP建图;最短路;状压DP。正解是枚举一个点然后状压DP,但是根据DP建图然后跑Dijkstra能过,甚至纯粹DP也能过。我写的是暴力DP,理论上复杂度不对,应该用Dijkstra优化。因为对时间复杂度的错误(实际上结果是正确的)估计,写了部分分,但是写挂了,不然100pts。
T3
容斥DP。想过容斥但是排除了。应将容斥DP作为trick记录。
T4
平衡树;KMP。有难度。没仔细想,但是何天成场切了,做法如下:
考虑KMP中维护的pre数组。如果一个以i结尾的区间对k的询问有贡献,那么i可以通过pre一直跳到k。建一棵树,从令点i的父亲是pre[i],那么询问i的答案就是i的子树大小。
何天成使用dfn序把树展开到线段上,并维护一个nxt[i]表示i的子树在线段上结尾的位置+1的位置对应的点,也就是这颗子树对应的区间结束后要去的下一个子树,也就是这颗子树右边的兄弟或是亲戚的编号。使用splay维护这个线段的nxt。
事实上,后半段可以用树链剖分做,这说明了树链剖分和dfn序+某数据结构(例如平衡树)是可以互相转化的。
改进措施
记录容斥DP作为trick。加强专注力。学习平衡树。向何天成请教考试技巧。开考后要快速切水题。
24/11/23
策略
基本没问题。注意先写简单的部分分,太复杂的部分分留到后面写。本次比赛问题还是控制力差了,由于T1过于简单直接摸了 😕 。
T1
很简单,一眼秒了,十分钟写完。
T2
线性DP;根据转移点优化DP。
本题暴力很好想,但是正解需要注意力。原式子 \(f_{i,j}\) 表示a匹配到了i,b匹配到了j,转移为 \(f_{i,j}=\max(f_{i-1,j}+1,f_{i,j-1}+1,f_{i-1,j-1}+1+\inf\times [a_i==b_j])\) 。注意到在不是 \(a_i==b_j\) 的地方都会使用最后一种转移,因此可以把转移点设置在所有的 \(a_i==b_j\) 的地方。\(dp_i\) 表示 \(pos_a-pos_b==i\) 的时的最小操作次数。
T3
需要玩样例发现一些性质,再想到并查集维护。
T4
不会。
改进措施
模拟赛注意力差出去休息,否则自罚一道紫题限当天写完,如果没完成那么停休闲娱乐1周(感觉我坚持不下来怎么办)。把T2整理,并整理DP相关题目。
24/11/25
T1
一个枚举思维:枚举答案的时候,如果答案不会太大,考虑只枚举答案不枚举不是答案的部分。
放在这道题上是一个trick:请问有多少对整数 \(x,y\) 满足 \(1\le x\le n\) 且 \(1\le y\le m\) 且 \(\exists k\in N_+, xy=k^2\) ;一个等价的问题 \((\sqrt x +\sqrt y)^2\in N_+\)。
做法有两种:线性的,把 \(a\) 质因数分解,设置一个集合 \(S\) 表示出现奇数次的质因子,那么能够与 \(a\) 匹配产生贡献的数的 \(S\) 一定和它的 \(S\) 相同。复杂度未知的(或许是 \(O(ans)\) ),枚举两个完全平方数 \(i^2,j^2\) 作为 \(x,y\) 的基底,然后枚举一个共同的倍数 \(k\) ,这样一来两个数字的质因数分解的奇次项就互相抵消了,预先统计上 \(x==y\) 的减少复杂度,最后注意 \(i==j\) 或者 \(gcd(i^2,j^2)>1\) 时应该不统计避免重复。另外还有一种思路我没看明白:teleport。
T4
这道题有点好写就是不好想。我们有 \(n-1\) 对禁止关系,禁止有一条路径同时经过一个禁止关系中的那两个点。统计路径总数。考虑如果一对禁止关系的两个 \(x,y\) 不呈现祖孙关系,那么禁止路径的一个端点在 \(x\) 的子树内,另一个在 \(y\) 的子树内;如果呈现祖孙关系,那么禁止路径的一个端点在 \(x\) 的反子树内,另一个在 \(y\) 的子树内。由于是子树操作,我们用dfn序把树摊开到线段上,建成一个二维图,两个坐标轴表示路径的起点和终点,那么我们相当于是禁止了 \(O(n)\) 个矩形。之后使用矩形面积并就可以了。
那么我们每个限制相当于 ban 掉了坐标系的 1个矩形。直接扫描线求矩形面积并即可。时间复杂度 \(O(n\log n)\)
24/11/26
T1
比 \(𝑎_1 + 𝑏_1\) 大的 \(𝑐_𝑖\) 数量的期望,可以转化为每一个 \(𝑐_𝑖\) 比 \(𝑎_1 + 𝑏_1\) 小的概率之和。
枚举 \(𝑥 ≠ 𝑎_1\),考虑对于 \(𝑎_𝑢 = 𝑥\) 的 \(𝑢\),满足 \(𝑎_𝑢 + 𝑏_𝑢 < 𝑎_1 + 𝑏_1\) 的概率。
此时注意到对于 \(𝑏_𝑢\) 仅有限制 \(1 ≤ 𝑏_𝑢 ≤ 𝑛 , 𝑏_𝑢 < 𝑎_1 + 𝑏_1 − 𝑥\) 以及 \(𝑏_𝑢 ≠ 𝑏_1\),所以容 易计算 \(𝑏_𝑢\) 能取到合法值的概率。
考虑加速这个过程,注意到当 \(𝑥\) 每次增加 \(1\) 时,\(𝑏_𝑢\) 合法解的个数要么不变,要么减少 \(1\) ,且可以拆成两个等差数列以及一个常数列。
复杂度瓶颈在于求逆元,实际上若使用离线求逆元的技巧,可以做到单次询问 \(𝑂(1)\)。
时间复杂度 \(𝑂(𝑇\log𝑛)\) 或 \(𝑂(𝑇+\log𝑛)\) 。
T2
想到了正解是dfs,但是一些细节没想清楚就开始写,导致写了将近一小时没写完。
T3
是原题,写的不慢。
改进
罚自己接下来3天内写2道紫题。注重细节。
2024.12.14 CF团队模拟赛
晚到1小时,只做了1题。rank 最后一名。非常差的成绩,考时非常摆及没状态,且策略出现严重失误。
本来应做题目:ABC,或许还有D。因为被A卡住所以投入了2小时,后来有些放弃。其实后面的题反而简单不少。
A(CF1536C)
非严谨的调和级数没有过,可能是远超我想象的复杂度。正解需要一点Attention:多个比率相同的字符串和起来还是比率相同。使用map维护即可 \(O(nlogn)\)。
B(CF1535D)
转化为一棵二叉树,01决定选取左儿子还是右儿子。由于深度只有18,维护 \(f_i\) 表示以 \(i\) 为根的子树中有多少能赢,暴力修改即可 \(O(n+qlogn)\)。
C(CF1526D)
Attention:每次加入两个数字,中位数至多距原来偏移 \(1\) 下标。因此每次加入一个数字时使用set判断距离原来的数字下标距离是否为 \(1\) 即可。
D(CF1535E)
E(CF1536E)
结论题,我███。总之,确定了哪些位置是 \(0\),就确定了其余位置为 \(距最近的0的距离\)。枚举哪些位置是 \(0\) 即可。
F(CF1536F)
转自DD(XYX)
不难推出一个结论:后手必胜( 2 ≤ N 2\leq N 2≤N)。原因是,最终不能走的时候,场上 A 和 B 的总数一定是偶数(反证法易证),意味着最后一个走的是后手。
而且这个结论强大的地方在于,不论你怎么走,只要最后必须无子可放,那么后手想输都输不了。
接下来,游戏进程就可以不用考虑博弈论的问题了。
我们枚举最终有多少个字母,然后剩余的空白就填入 AB 之间,再确定哪些是甲走的哪些是乙走的,最后确定每个人放的字母的相对顺序。
中间乘 2 是因为 ABAB... 和 BABA... 都有可能。
24/12/21
T2
记得多测一定要清空(初始化)。(-20pts)
题目:
给你一个序列,要求你划分为若干段,每种划分方案的价值为:每段的异或和的乘积,求所有方案的价值的平方和。
解法:
首先有 DP 式子 \(f_i=\sum\limits_{j=0}^{i-1}f_j(s_i\oplus s_j)^2\) 其中 \(s_i\) 表示异或前缀和。
其次对该式子做按位分解,每一位单独异或(这一点在数据范围里有提示),并且把前缀和拆成一个求和式,把平方拆成两个“多项式”相乘,拆括号得到两层 \(\log W\) 即位数级的循环。由此有 \(O(n\log^2 n)\) 的做法,但是具体的式子我忘了。(htc太巨了)
T3
DP 初始值赋值错了(悲)(-25pts)
题目:
给你一个序列,你要分为若干段,每段长度为 \(1\) 或者 \(2\). 你要让每段和的极差最小。
解法:
根据某区间DP题目的经验,显而易见的有 \(dp_{i,j}\) 表示前 \(i\) 位最大值小于等于 \(j\) 的时候,最大的最小值。
然鹅正解有两条路。首先都要离散化地枚举最小值(或是最大值),其次都是线段树,一种维护DP,一种纯线段树。纯线段树的做法:对于每个区间维护:全局最优极值、去除左边第一个点后的最优极值、去除右侧第一个点的最优极值、去除左右侧第一个点的最优极值。转移的时候要考虑把位于区间分界点的两个元素合起来的方案。每次最小值升高,就把失效的点设置为inf(但是其本身的值不这么改,也就是说合起来还是没问题的,但是单独选就有问题了),显然如果全局最优极值为inf就说明此种情况下无解,break即可。
24/12/28
T1
题意
给你一个序列。对于这个序列的每个前缀,求出:最多可选取多少个数,使得选取的数中没有两个差恰好为 \(k\) 的。
解法
题解里怎么讲了个最大独立集,看不懂啊。其实不应该看不懂,就是这个名词有点误导人,使kazuha学姐往最长反链那方面想了。(虽然但是,这是无向图,不存在“最长反链”这一说啊)
那么正解很简单,注意到如果我们把差为 \(k\) 的点连一条边,有边相连的就不能同时选。更进一步地,一个大小为 \(x\) 的连通块中至多能选 \(\lceil\frac{x}{2}\rceil\) 个数。并查集维护一下即可,每次加一个数进来。
T2
题意
给你一个序列,初始为全 \(1\),正面朝上.你可以:
- 让单个数“翻面”
- 让区间内所有正面朝上的数字 \(\times x\)
- 如果区间内所有数字(不论哪一面朝上)均是x的倍数,那么令此区间内正面朝上的数字 \(\div x\)
请你输出每次叁号操作是否成功。\(1\le x\le 30\)
解法
先质因数分解,只有10个质数。然后线段树维护正面朝上的的min和反面朝上的min(注意反面min初始是inf)最后一操作只要交换这两个值就可以了。
25/1/7
终于重回第一了呜呜呜(虽然是因为htcT1爆零了
T2
题意
你在玩跳棋,现在有一个 \(n\times m\) 的矩形中放满跳棋,矩形四周有一圈空的位置,问你最多吃多少棋子。
解法
注意到
x
ooo
o
(o是棋子,x是空格,空的表示没有要求)
可以变为
x
xxx
o
需要3步。
可以证明这样是最优秀的,所以按照 \(n,m\) 对 \(3\) 取模的结果分类讨论即可。
T3
题意
n 个数字,每个都等概率的是 \([1,m]\) 中的一个整数,求众数出现次数的期望。
m很大,n很小。
解法
使用计数DP。\(dp_{i,j,k}\) 代表前 \(i\) 种数字,占据了 \(j\) 个位置,众数出现次数是 \(k\) 的解。复杂度 \(O(mn^2)\)。但是把第一维压成 \(n\),最后用组合数处理一下就会使速度变得很快。
25/1/8
T2
给你一个01串,从中选出两个子段,把他们的结尾对齐,然后异或,这样做最大值是多少。
解法一:
[0..00][11..11][00..00][11001010...]
I II III IV
将数列划分为这样四段。显然除非少数特例(后文一样),否则第一个子段一定会包含 II、III、IV 这三段。(因为让尽可能高的位数为 1 是更优秀的)
同理也尽量让 III 段的前面的位置尽可能产生 1,而由于两个子段是向后对齐,所以第二个子段的长度一定大于等于 III、IV三段合起来的长度。所以说第二个子段的开头一定在 I、II 段中,并且 111 的部分应尽可能的长。
为了不消除IV段开头的内容,我们还要求 IV 段刚开头是0. 结合以上限制就能得出最大值,复杂度 O(n).
解法二
分类讨论还是太复杂了,把 II、III、IV 段合起来按位翻转,然后用 KMP 匹配即可。
改进措施
不得摸鱼。
25/1/13
T2
我是奶龙,在多个错误思路横跳了2.5小时后发现这题是区间DP板子,这辈子有了。
T3
注意到这是一颗内向基环树,缩点后可以树形 DP。我们直接做线段树合并。
25/3/23
T1因为没看见保留4位小数而挂了80pts。
T3
给定一个序列,每次可以消除其中一个数,要求每次消除完之后不能有相邻两个数相同,求消完的方案数。
做法:区间 DP,从后往前转移,用组合数枚举顺序。
AC代码
#include <iostream>
using namespace std;
typedef long long ll;
const ll N=500+10;
const ll mod=998244353;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll dp[N][N],a[N],c[N][N];
ll n;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
ll cnt=0,pos=0;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]==a[i-1])
{
cnt++;
pos=i;
}
}
if(cnt>1)
{
cout<<0<<endl;
return 0;
}
if(cnt)
{
for(ll i=pos;i<n;i++)
a[i]=a[i+1];
n--;
}
a[n+1]=inf;
for(ll i=0;i<=n;i++)
dp[i+1][i]=1;
c[0][0]=1;
for(ll i=1;i<=n;i++)
{
c[i][0]=c[i-1][0];
for(ll j=1;j<=n;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
for(ll len=1;len<=n;len++)
{
for(ll l=1;l+len-1<=n;l++)
{
ll r=l+len-1;
for(ll s=l;s<=r;s++)
{
if(a[l-1]!=a[r+1])
{
(dp[l][r]+=dp[l][s-1]*dp[s+1][r]%mod*c[len-1][s-l]%mod)%=mod;
}
}
}
}
if(pos)
cout<<dp[1][n]*2%mod<<endl;
else
cout<<dp[1][n]<<endl;
return 0;
}
T4 开心麻花
某次模拟赛的原题没写出来。
题面可以简化为求一个矩阵里,以每个点为右下角的方框有多少,有障碍物的地方不饿能选作方框。(\(n\le 300\))。
线段树扫描线,枚举 \(i,j\),移动 \(j\) 的时候,维护竖向扫描线,位置 \(k\) 表示以 \((k,j)-(i,j)\) 为右边界的矩形有多少,当 \(j\) 右移时,障碍物位置上的贡献会被置 \(0\),我们预处理每个点向上能延伸的长度,然后在扫描线上区间加即可。
25/5/20 NOI模拟赛
思考了2小时发现T1可以模拟退火,得了98分,北中oj rank1,但是海亮oj让范家俊的模拟退火没有tle,他得了90+25胜过了我。总之,赢!
那么我就不说T1的题面了,因为正解是线性规划,听不懂。不过模拟退火经过调整也可以AC。
25/5/21 模拟赛
没认真打,略。T1是杜教筛,但是我没学过。范家俊学过,他太强了。
25/5/25 信友队图灵杯
https://contest.xinyoudui.com/contest/425#/Introduction
T1
每个人在 \(s_i\) 时刻从零点出发,每秒往前走 \(1\) 单位,直到到达 \(p_i\),然后在这个位置停留 \(a_i\) 秒。停留的人会挡住后面的人,使他们必须等到自己停留结束再继续,人没有体积。求每个人到达自己目标的所需时间。
化成二维图就很好理解。首先我们先把整个图做一个变换,减去一个 \(y=x\) 的直线,使得所有人的路径成为横线而非斜线。(方便观察)接下来我们注意到一个人到达目标的时间等于他出发时间后第一个“阻碍”大于目标的时间。又注意到一个人出发时间后“阻碍”肯定越来越远,因此到达目标的时间就是所有阻碍的结束时间的最大值。二维数点即可。
T2
给定一张 \(n\) 个点 \(m\) 条边,每条边带非负边权的无向图。节点编号为 \(1,2,⋯,n\)。
接下来有 \(q\) 次询问,每次询问给出三个整数 \(u,v,w\)。你需要判断是否存在一条从节点 \(u\) 到节点 \(v\) 的路径,使得其边权和为 \(w\) 的倍数。
我们这里认为整数 \(a\) 是整数 \(w\) 的倍数,当且仅当存在整数 \(k\),使得 \(a=kw\)。
给定的图可能有重边、自环,路径也可反复经过重点、重边。
\(1\le n,q\le 10^6,1\le w\le 2^{60}-1\)
待补。可以看上面的链接。
25/6/2 模拟赛
T1
给定一个树。设对于树上简单路径 \(u\rightarrow v\) ,定义 \(near(x)\) 表示路径上距离 \(x\) 最近的点,则这条路径的价值定义为 \(\frac{1}{n}\sum\limits_{x=1}^ndis(near(x),v)\),求有多少有向路径满足价值 \(\ge\frac{1}{2}dis(u,v)+k\),其中 \(k\) 是给定的值。
一道拼好题。
推式子得到 \(\sum\limits_{x=1}^n(dis(near(x),v)-dis(u,near(x)))\ge 2nk\)。注意到可以转化为 \(\sum\limits_{x=1}^n(dis(x,v)-dis(u,x))\ge 2nk\)。那么我们就可以用 \(f_i\) 表示所有点到 \(f_i\) 的距离和,然后二维数点就做完了。
25/6/3
T1
一次 \(k\) 型操作定义为,把序列中前 \(k\) 大的元素减去 \(1\)(大小相同的编号小的优先)。给定一个序列,先询问对该序列做 \(m_0\) 次 \(k_0\) 型操作的值(不会影响序列,只是求值),然后若干互不干扰(不影响原序列)的询问:(一)单点修改序列的值,(二)询问 \(m_i\) 次 \(k_i\) 型操作后第 \(x\) 大的元素是什么,(三)同上一个询问,但是问的是第 \(l\) 到 \(r\) 大的元素和。
有 \(50%\) 数据不包括询问。
对于不包括询问的部分分,注意到这些元素一定有一些被操作了 \(m_0\) 次,有些不到 \(m_0\) 次。二分 \(m_0k_0\) 次 \(-1\) 能让操作了不到 \(m_0\) 次的元素被下降到什么高度,然后剩下的 \(-1\) 次数肯定只能对 \(>=m_0\) 的元素操作,因此剩下的操作每个元素至多被操作一次,那么就这样做一下,的除了最终结果。
25/6/8
T1 和 T3 都是原题,略过。
T2
有若干个三元组 \((a_i,b_i,c_i)\),你要把她们划分为 ABC 三组,要求最小化 \(\max\limits_{x\in A}\{x.a_i\}+\max\limits_{x\in B}\{x.b_i\}+\max\limits_{x\in C}\{x.c_i\}\)。
首先考虑二元组的情况,显然我们可以把它们按照 \(a_i\) 排序,然后维护一个 \(b_i\) 单调下降的阶梯形上边缘,这些边缘的向下的拐点就是可能的答案。到三元组里,我们从大到小扫描 \(a_i\),维护 \((b_i,c_i)\) 构成的阶梯形上边缘,每次挪动就把一个二元组插入这个阶梯形上边缘,用 set 维护即可。htc太强了。
附代码
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll N=3e5+10;
ll n,T;
struct node
{
ll a,b,c;
}a[N];
set<pair<ll,ll>> s;
multiset<ll> re;
void del(set<pair<ll,ll>>::iterator x)
{
auto y=x,z=x;
y--;z++;
re.erase(y->first+x->second);
re.erase(z->second+x->first);
re.insert(y->first+z->second);
}
void add(set<pair<ll,ll>>::iterator x)
{
auto y=x,z=x;
y--;z++;
re.erase(y->first+z->second);
re.insert(y->first+x->second);
re.insert(z->second+x->first);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>n;
for(ll i=1;i<=n;i++)
{
cin>>a[i].a>>a[i].b>>a[i].c;
}
sort(a+1,a+n+1,[&](node x,node y){return x.a>y.a;});
a[n+1]={0,0,0};
s.clear();
s.insert({0,inf});
s.insert({inf,0});
re.clear();
re.insert(0);
ll ans=a[1].a;
for(ll i=1;i<=n;i++)
{
// cout<<a[i].a<<" "<<a[i].b<<" "<<a[i].c<<endl;
auto pin=s.insert({a[i].b,a[i].c}).first;
add(pin);
pin++;
if(pin->second>=a[i].c)
{
del(s.find({a[i].b,a[i].c}));
s.erase({a[i].b,a[i].c});
}
else
{
while(1)
{
auto pos=s.find({a[i].b,a[i].c});
pos--;
if((*pos)==make_pair(0ll,inf) || pos->second>a[i].c)
break;
del(pos);
s.erase(pos);
}
}
// for(auto xx:s)
// cout<<"("<<xx.first<<","<<xx.second<<") ";
// cout<<endl;
// for(auto xx:re)
// cout<<xx<<" ";
// cout<<endl;
ans=min(ans,a[i+1].a+(*re.begin()));
}
cout<<ans<<endl;
}
return 0;
}
T4
给出一个 \(01\) 矩阵,每次操作把一个 \(0\) 变成 \(1\) ,每次操作后求最大的全 \(0\) 矩形。要求 \(O(nq)\)
正着做不好做,考虑时光倒流。显然每次要么保持原答案,要么新答案包含新增的 \(0\)。首先用著名的 DP 算出最后的答案。然后,用双指针(保持长度小于等于宽度)扫描新答案在纵坐标上的上下界(预先处理每个点左右能延伸的最大全 \(0\) 距离,这样就能 \(O(1)\) 判定一行全是 \(0\) 的宽度),大致是这样。sxht太强了。
2025/6/10
T1
称呼一个 \(n\) 长度的排列为合法,当且仅当做以下操作能使其变得有序:
对于 \(p_j=1,2,3\dots\) 依次 到 \(n\),将 \(p_j\) 与其前或后位置交换,或者不动。
求出长度为 \(n\) 的合法序列中字典序第 \(k\) 大的。
首先注意到 \(1\) 不能位于 \(p_3\) 及以后,否则无法被交换到开头。假使 \(1\) 位于 \(p_3\),那么 \(p_1=2\),\(p_2\) 无所谓;还有 \(1\) 位于 \(p_1\) 和 \(p_2\) 的情况。如果把 \(p_1=2,p_2=1\) 划分到第一种情况,那么这三种情况的字典序就有了明显的区分,然后每种情况在去掉 \(1\) 或者 \(1,2\) 后可以被转化为子问题,因此可以递归求解。
2025/6/11
T1
数位 DP。\(dp_{i,j}\) 表示长度为 \(i\) 及以下的 \(\bmod k=j\) 的数字数(包含 \(0\))这个可以枚举下一位,从低到高很容易的维护,但是为了去重,上一层的 \(dp_{i-1,0}\) 只能保留 \(1\) 转移到下一层,剩下的直接计入答案而不转移到下一层。保留 \(1\) 是因为 \(0\) 不能计入答案,所以转移到下一层再计入答案。
T2
首先注意到除了主角之外的所有人都一样,因此可以只算出一个人的情况。接下来 \(dp_{i,j}\) 表示在前 \(i\) 场拿到 \(j\) 分的概率,用前缀和或者什么东西优化一下可以做到 \(O(n^2m)\)。
2025/6/18
T1
给出一些字符串,求最长的至少是其中两个字符串的公共前缀的字符串,如有多个输出字典序最小的,然后再对后缀也回答这个问题。
首先我就想到了 SA,首先把这些字符串连接在一起(中间夹上不同的分隔符),然后找出所有字符串的开头在 SA 数组中所在位置,求相邻两个之间的 LCA(\(min{height}\)),找出其中的最大值就是答案。(考场上想错了)输出方案数可以答案位置相邻的 \(\ge ans\) 区域内有多少字符串的开头。到了后缀时就不能简单地颠倒每个字符串,因为字典序不对,所以要先求出答案,再把所有长为 \(ans\) 的后缀塞进 map 中,找出最小的出现次数 \(\ge 2\) 的字符串。
T2
用 KMP,不断枚举 \(nxt[n],nxt[nxt[n]],\dots\),看看这个值是否在 \(nxt[2]\dots nxt[n-1]\) 中出现,如果出现则是答案。
T3
求出根到每个位置的hash值,然后做子树数颜色即可。还有一种做法是把字符相同的儿子合并,做到最后只需统计子树大小即可。注意哈希冲突,最好用双模哈希。
T4
给定一个仅包含左括号和右括号的序列,可以对该序列执行以下两种操作:
1、将序列的最后一项移动到首项
2、在序列任意位置添加一个左括号或者右括号你的任务是使用上述两种操作将给定的括号序列变成一个合法的括号序列,并且使得括号序列的长度最短,如果有多个长度最短的方案,那么输出字典序最小的那一个(左括号的字典序比右括号小)
注意到我们可以先做完所有2操作再做1操作。我们又注意到,左右括号数量相等时,一定可以通过2操作得到一组解,因此1操作数目由左右括号数量决定,且添加的左括号一定在序列开头,添加的右括号一定在序列结尾。我们视左括号为 \(+1\),右括号为 \(-1\),求前缀和。我们把序列翻倍,在上面滑动窗口,如果区间内 \(s_{min}+添加的左括号数\ge 0\) 那么这个序列合法。用二分哈希比大小就行。SA会TLE。
2025/09/23
不够集中注意力,导致T1的签到题花了2h。T2数组开小挂了25pts。
T1
给出一个 \(n\times m\) 的网格图,第一秒 \((1,1)\) 被点亮,画一条对角线,此后每一秒,在对角线下方的点可以点亮其左下、下、右下方的点,在对角线上方的点可以点亮其右上、右、右下方的点。第 \(I\) 层的点要在第 \(i\) 秒被点亮,不能交叉点亮。问方案数。\(1\le n\le 5\times 10^6,1\le m\le 10^{18}\)
注意到对角线两侧是对称的,只考虑一边。令 \(dp_{i,j}\) 考虑上一行前 \(i\) 个数,后一行前 \(j\) 个数(必须填满)的方案数。这样做存在很多冗余的状态,所以考虑把 \(j\) 限定在 \([-1,0,1]\) 表示后一行前 \(i+j\) 个数被填满。
T2
给出一个 \(3000\times 3000\) 的字符矩阵,求面积最大的矩形,满足存在一行 和 一列是回文串。
先用manacher求出每个位置为中心的横向和纵向的回文串最大长度(我写了个4倍常数写法)。接下来,可以扫描线+set(使用可删堆代替以卡常数),或者单调队列,或者并查集维护颜色段

浙公网安备 33010602011771号