2026 牛客寒假集训营-6
1. K-小L的游戏1
题目描述
小 L 的科研毫无进展,于是他开始和 fallleaves01 玩游戏。
游戏在一个初始值为
的变量
上进行。小 L 和 fallleaves01 轮流对
进行累加操作,小 L 先手。
每一轮分为两个回合,具体规则如下:
小 L 的回合:将
的值更新为
;
fallleaves01 的回合:将
的值更新为
。
一旦满足
,游戏立即结束,否则继续进行下一轮。现在小 L 想请问你,最后一次操作是谁进行的?
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数
代表数据组数,每组测试数据描述如下:
在一行上输入三个正整数
,表示小 L 的增量、fallleaves01 的增量、目标阈值。
输出描述
将所有测试数据的答案按顺序拼接,在一行内连续输出(中间不加空格或换行)。如果最后一次操作是小 L 进行的,输出
;如果最后一次操作是 fallleaves01 进行的,输出
。
解题思路
找到边界 res = (floor(z/(m+n)))*(m+n),判断一下最后一次
AC 代码
void solve(){
cin>>m>>n>>z;
int k=z/(m+n);
int res=k*(m+n);
if(res<z&&res+m>=z){
cout<<0;
return ;
}
else{
cout<<1;
return ;
}
}
2. G-小L的散步
题目描述
小L的科研毫无进展,于是他决定出门散散心。
他看见地上的石块所铺就的道路,不由得想起来小时候经常玩的游戏:不能踩中两个石块之间的缝隙,不然就输了。
现在小L面前有
个石块,第
个石块的长度为
,每两个相邻石块之间都有一个长度不计的缝隙。
小L一共走了
步,每步跨过的长度为
。
小L的初始位置为
,最左边的石块的位置也为
,脚掌的长度为
,脚后跟位于
的位置,请问小L在散步的过程中是否有踩中石块的缝隙(最后一个石块的右端点也视为缝隙)。检查范围包含初始位置以及每一步走完后的位置,脚掌边缘位于缝隙不视为踩中。
输入描述
第一行输入三个正整数
,表示石块的数量,走的步数和脚掌的长度。
第二行输入
个正整数
,表示每个石块的长度。
第三行输入
个正整数
,表示步子的大小。
输出描述
如果小L在散步的过程中有踩中石块的缝隙,输出
,否则输出
。
解题思路
把每一个缝隙都存起来,排序,把每一步的LR处理出来,对于每一个LR区间,二分找到最近的缝隙,检查一下有没有踩到即可
AC 代码
void solve(){
cin>>n>>m>>L;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]+=a[i-1];
}
for(int i=1;i<=m;i++){
cin>>b[i];
}
vector<array<int,2>>v;
v.push_back({0,L});
for(int i=1;i<=m;i++){
auto [l,r]=v.back();
v.push_back({l+b[i],r+b[i]});
}
for(auto [l,r]:v){
int idx=lower_bound(a+1,a+1+n,l+1)-a;
int val=a[idx];
if(val>l&&val<r){
cout<<"YES\n";
return ;
}
}
cout<<"NO\n";
return ;
}
3. H-小L的数组
题目描述
小 L 的科研毫无进展,于是他写下了两个数组。
给定两个长度均为
的数组
和
。小 L 拥有一个初始数值
,且初始时
。
接下来小 L 需要依次进行
次操作。在第
次操作中,小 L 必须从以下两种变换规则中选择一种执行:
规则一:将
更新为
,即
减去
后与
取最大值。
规则二:将
更新为
,即
与
进行按位异或运算。
请计算在执行完所有
次操作后,
可能达到的最大值。
输入描述
第一行输入一个正整数
,表示数组长度。
接下来一行输入
个非负整数
,表示
数组。
接下来一行输入
个非负整数
,表示
数组。
输出描述
输出一个非负整数,表示
次操作后
的最大可能值。
解题思路
由于异或操作,最大值不超过4095,数据量很小,直接动态规划跑一遍就OK。
AC 代码
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
dp[0][0]=true;
for(int i=1;i<=n;i++){
for(int j=4096;j>=0;j--){
if(dp[i-1][j]){
dp[i][max(0ll,j-a[i])]=true;
dp[i][j^b[i]]=true;
}
}
}
for(int i=4096;i>=0;i--){
if(dp[n][i]){
cout<<i<<"\n";
return ;
}
}
}
4. A-小L的三角尺
题目描述
小 L 的科研毫无头绪,于是他开始玩他的直角三角尺。
小 L 拥有
把直角三角尺,第
把尺子的两条直角边长度分别为
和
。小 L 突然灵光一现想到了一个有趣的问题:
对于第
把尺子,小 L 可以选择一个非负整数
作为打磨掉的长度(即打磨后的直角边长变为
)。该操作必须满足
,即打磨长度不能超过原边长。
特别地,当
时,打磨后该直角边长度变为
,此时三角形退化为一条线段,其斜边长度等于另一条直角边的长度,即
现在小 L 拥有总共
的打磨额度,所有尺子的打磨长度之和必须满足
。请问在最优策略下,这
把三角尺的斜边长度之和最小可以是多少?
输入描述
第一行输入两个正整数
,分别表示直角三角尺的个数和总打磨额度。
此后
行,第
行输入两个正整数
,表示第
把直角三角尺的两条直角边长度。其中
为可打磨的直角边。
输出描述
输出一行一个实数,表示所有直角三角尺打磨后的斜边长度之和的最小值。
由于实数的计算存在误差,当误差的量级不超过
时,您的答案都将被接受。具体来说,设您的答案为
,标准答案为
,当且仅当
时,您的答案将被接受。
解题思路
把每一次打磨一个单位长度后产生的贡献作为关键字排序,每一次选择贡献最大的三角尺进行打磨
PS
想二分枚举打磨的长度,但是精度存在问题,浮点数的题慎用二分吧
AC 代码
void solve(){
cin>>n>>w;
priority_queue<array<ld,3>>q;
ld ans=0;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i];
ans+=cal(x[i],y[i]);
if(y[i]>0.5)q.push({cal(x[i],y[i])-cal(x[i],y[i]-1),x[i],y[i]-1.0});
}
while(w>0&&q.size()){
auto [gx,x,y]=q.top();q.pop();
ans-=gx;
w--;
if(y>0.5){
q.push({cal(x,y)-cal(x,y-1),x,y-1.0});
}
}
cout<<fixed<<setprecision(20)<<ans<<"\n";
}
5. D-小L的扩展
题目描述
小 L 的科研毫无进展,于是他开始在纸上画方格。
小 L 的纸由
行
列共
个白色方格组成,其中有
个方格被染黑了,染黑的方格每个单位时间会向上下左右四个方向扩散一格。其中有
个方格上有蓝色的墨水,每个染上蓝色的墨水的方格会在某个特定的时间点变为白色,在蓝色墨水存在的时刻是不能被染黑的方格扩散到的。
小 L 想知道这张纸上的方格什么时候被完全染黑,请你帮帮他。
更具体地:
记初始时刻为
,给出的
个格子在时刻
已经是黑色;给出的
个格子从时刻
起为蓝色,直到时刻
到来时先变为白色。
对每个整数时刻
,先让所有满足
的蓝色方格变为白色,然后黑色从所有已变黑的方格向上下左右四邻方格各扩散
格;若目标方格在该时刻已为白色,则在该时刻被染黑。
输入描述
第一行输入四个正整数
,表示纸张大小、黑方格个数、蓝方格个数。
此后
行,第
行输入两个正整数
,表示第
个黑方格的位置;
此后
行,第
行输入三个正整数
,表示第
个蓝方格的位置及其变为白色的时间。
保证
个被染色的方格两两不同,即不存在同一个方格既被染黑又被染蓝。
输出描述
输出一个整数,表示整张纸被完全染黑的时间。
解题思路
BFS解决,只是不能以层为单位,要以时间为顺序,把时间为第一排序的关键字,可以实现时间的顺序性染色,也不用考虑时间中间的过程,会产生跳跃性的效果。
AC 代码
void bfs(){
while(!q.empty()){
auto [dis,x,y]=q.top();q.pop();
if(dis!=res[x][y])continue;
for(int i=0;i<4;i++){
int sx=x+dx[i],sy=y+dy[i];
if(!safe(sx,sy))continue;
int t=max(dis+1,tim[sx][sy]);
if(t<res[sx][sy]){
res[sx][sy]=t;
q.push({t,sx,sy});
}
}
}
}
6. C-小L的线段树
题目描述
小L的科研毫无进展,于是他种了一棵线段树。
在接下来
天里,每天会发生以下两类事件之一:(除叶子节点外的)树上某节点的信息被毁坏,或查询某个区间在该线段树上的代价。
线段树的定义:将线段树视为一棵二叉树,每个节点对应一个闭区间
:
若
,则该节点为叶节点。
若
,令
。该节点的左子节点对应区间
,右子节点对应区间
。
根节点对应区间
。
受损与信息获取:若某节点被毁坏,其信息可通过访问其左右两个子节点得到(即查询时遇到该节点则必须递归到子节点,该节点本身不提供信息)。
查询代价:给定区间
,从根节点开始进行区间查询,过程如下:
若
被
完全包含:
若该节点未被毁坏:计
次「有效访问」,并停止递归(该分支结束);
若该节点已被毁坏:不计数,直接对与
有交的子节点按上述规则递归访问。
若
未被
完全包含:
若该节点未被毁坏:计
次「有效访问」;然后若左子节点对应区间与
有交则访问左子节点,若右子节点对应区间与
有交则访问右子节点;
若该节点已被毁坏:不计数,直接对与
有交的子节点按上述规则递归访问。
定义
为完成对
的查询时,被计数的「有效访问」节点个数(即访问到的、未被毁坏的节点个数)。
对于每个查询事件,你需要回答:在当前已发生的毁坏状态下,查询给定区间
的
。
【名词解释】
二叉树:满足以下全部条件的树。
二叉树可以是空集;若不为空,则由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成;
每个节点要么没有父节点连接(此时该节点被称为根节点)、要么被
个父节点连接(此时该节点被称为父节点的子节点);
每个节点连接的子节点数量要么为
(此时该节点被称为叶子节点),要么不超过
,且此时每个子节点都有明确的“左”或“右”属性。
输入描述
第一行输入一个正整数
,同时用于表示事件总数和线段树的根节点对应的区间长度。
此后
行,第
行先输入一个整数
,表示第
天发生的事件类型,随后在同一行:
若
,输入两个整数
,表示毁坏了
区间所代表的节点,保证仅对应线段树上的一个节点,同一个节点可能被毁坏多次;
若
,输入两个整数
,表示想要获取
区间的信息。
输出描述
对于每一个
的事件,新起一行输出一个整数,表示
的值。
解题思路
每一次都查询,时间上无法通过,根据线段树的性质,提前处理好每一个节点的查询值,没有破坏的节点为1,破坏的节点从左右子节点中更新,线段树模板的题
AC 代码
void update(int p,int l,int r,int L,int R){
if(l==L&&r==R){
tr[p].f=0;
tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
return ;
}
int mid=(l+r)>>1;
if(R<=mid)update(p<<1,l,mid,L,R);
else if(L>mid)update(p<<1|1,mid+1,r,L,R);
else{
update(p<<1,l,mid,L,R);
update(p<<1|1,mid+1,r,L,R);
}
if(tr[p].f==1)tr[p].val=1;
else tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
}
int query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R){
return tr[p].val;
}
int res=0;
if(tr[p].f)res++;
int mid=(l+r)>>1;
if(R>mid)res+=query(p<<1|1,mid+1,r,L,R);
if(L<=mid)res+=query(p<<1,l,mid,L,R);
return res;
}
7. I-小L的构造2
题目描述
小L的科研毫无进展,于是他开始研究构造。
小L有一个正整数
,他想构造一个长度为
的排列
![]()
,使得该排列中任意相邻的三个元素中,都至少存在两个数不互质
。
换言之,对于任意
,在集合
中存在两个不同的数
,满足
。
如果存在这样的排列,请输出任意一个符合条件的方案;若不存在,直接输出
。
【名词解释】
长度为
的排列
:由
这
个整数、按任意顺序组成的数组(每个整数均恰好出现一次)。例如,
是一个长度为
的排列,而
和
都不是排列,因为前者存在重复元素,后者包含了超出范围的数。
互质
:多个整数的最大的共有约数(简称最大公约数,gcd)如果恰好为
,被称为它们为互质的。例如,
和
的公约数有
,其中最大的约数是
,所以它们不是互质的;
和
的公约数仅有
,所以它们是互质的。
输入描述
输入一个正整数
,表示排列的长度。
输出描述
如果不存在符合条件的排列,直接输出
。否则,在一行上输出
个整数,表示构造出的排列,整数之间用空格分隔。
如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
解题思路
题解是以12个数为一组,边界情况特殊讨论一下。但是我对于边界情况的理解不太好,并没有使用分组的做法。
把不存在可以分在一起的质数看作孤立数,把其他的数字按照最小质因子分组,然后不同的质因子边界使用2的倍数填充,之后把孤立数分散地插入分组有序的数组中,得到答案
AC 代码
void solve() {
int n;cin >> n;
if (n == 3 || n == 5) {cout << -1 << "\n";return;}
vector<int> s = {1}, v[N];
for (int i = 3; i <= n; i += 2) {
if (2 * spf[i] > n) s.push_back(i);
else v[spf[i]].push_back(i);
}
vector<int> b, used(n + 1, 0), evens;
for (int i = 3; i <= n / 2; i++) {
if (spf[i] == i && !v[i].empty()) {
b.push_back(2 * i);
used[2 * i] = 1;
for (int x : v[i]) b.push_back(x);
}
}
for (int i = 2; i <= n; i += 2)if (!used[i]) evens.push_back(i);
evens.insert(evens.end(), b.begin(), b.end());
b = evens;
if (3*s.size() > n + 2) {cout << -1 << "\n";return;}
vector<int> res;
int si = 0, m = b.size();
if (si < s.size()) res.push_back(s[si++]);
for (int i = 0; i < m; i++) {
res.push_back(b[i]);
if (si < s.size() && i % 2 == 1 && i < m - 1) {
res.push_back(s[si++]);
}
}
if (si < s.size()) res.push_back(s[si++]);
for (int i = 0; i < res.size(); i++) {
cout << res[i] << (i == res.size() - 1 ? "\n" : " ");
}
}
8. J-小L的字符串
题目描述
输入描述
输出描述
PS
这个区域以后再探索吧
我不会NTT啊
AC 代码
9. L-小L的游戏2
题目描述
输入描述
输出描述
PS
这个区域以后再探索吧
我真会矩阵快速幂,但我想吃吃吃了
AC 代码
10. F-小L的极大团
题目描述
输入描述
输出描述
PS
这个区域以后再探索吧
我最讨厌推式子了,其实我是懒狗,我要去写点CF水题了,我不进步,沃兰多
AC 代码

京公网安备 11010502036488号