Codeforces Round 951 (Div. 2) 题解
本文网址:https://www.cnblogs.com/zsc985246/p/18236377 ,转载请注明出处。
好久没更新了,诈尸一下。
挑战最快题解。
B 题刚做完的时候网络爆炸了,掉了一点分。最终排名 22。
题目做起来很爽。大爱思维与构造!抵制数据结构!
2024/6/7update:修改了一些抽风表述(原谅赛时脑子不好使)。
2024/6/9update:更新 F 题解。
传送门
A.Guess the Maximum
题目大意
给定一个长度为 的数组 ,求一个最大的数 ,使得任意一个长度不小于 的连续子串的最大值大于 。
多组测试,。
思路
最大值尽可能小,那么选择的连续子串越短越优。
枚举所有长度为 的连续子串,求出它们最大值的最小值,然后减去 输出即可。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
ll n,m,k;
ll a[N],b[N];
void mian(){
ll ans=1e9;
scanf("%lld",&n);
For(i,1,n){
scanf("%lld",&a[i]);
if(i>1)ans=min(ans,max(a[i],a[i-1]));
}
printf("%lld\n",ans-1);
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
B.XOR Sequences
题目大意
给定两个数 ,构造无限长度序列 。求两个序列的最长公共子序列长度。
多组测试,。
思路
考虑公共子序列如何形成。
假设现在满足 。如果想要 自增之后仍然满足条件,那么在自增过程中, 的每一个二进制位要么同时改变,要么同时不变。
然后考虑 的一个满足条件的解:。我们将 所有相同的位全部变为 ,得到 的最小解。
此时 能够自增的次数显然是最多的。如果此时 的二进制低位连续的 的个数为 ,那么答案就是 。
也就是说,令 从最低位开始的连续相同二进制位的个数为 ,那么答案即为 。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
ll n,m,k;
ll a[N],b[N];
void mian(){
ll ans=0;
scanf("%lld%lld",&n,&m);
while(((n>>ans)&1)==((m>>ans)&1))++ans;
printf("%lld\n",1ll<<ans);
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
C.Earning on Bets
题目大意
给定一个长度为 的序列 ,你需要构造一个长度也为 的序列 ,满足 。
无解输出 。
多组测试,。
思路
显然让所有的 都相等时最优。
求出 的最大公倍数,然后计算验证是否合法即可。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
ll n,m,k;
ll a[N],b[N];
void mian(){
ll ans=0;
scanf("%lld",&n);
For(i,1,n){
scanf("%lld",&a[i]);
if(i==1)ans=a[i];
else ans=ans/__gcd(ans,a[i])*a[i];
}
ll s=0;
For(i,1,n){
b[i]=ans/a[i];
s+=b[i];
}
if(ans<=s){
printf("-1\n");
return;
}
For(i,1,n){
printf("%lld ",b[i]);
}
printf("\n");
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
D.Fixing a Binary String
题目大意
给定一个长度为 的 01 串 和一个整数 ,你需要恰好进行一次操作:
- 选择一个 ,将序列变为 。
定义一个 01 串 是好的当且仅当满足以下条件:
-
。
-
。
求是否能将 变为好串。如果可以,输出 ,否则输出 。
多组测试,, 是 的倍数。
思路
发现操作后 总在最后,结合序列的最终条件,考虑从这里入手。
令 表示 。
为了满足序列的格式, 以及 必定是好的 01 串,因为这些地方是操作无法影响到的。
分类讨论。
从前往后看,找到第一个使好串条件不成立的位置 , 为 。
-
,此时的连续 的个数没有到达 个,从中间划开必定不符合条件,只能选择 。
-
,此时连续 的个数超过 个,必须从中间划开,所以求出 结尾的连续 个数 ,然后选择 。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
ll n,m,k;
ll a[N],b[N];
ll check(ll ans){//验证b数组是否是好的
ll t=b[1],cnt=1;
For(i,2,n){
if(b[i]==t&&cnt<m)++cnt;
else if(b[i]!=t&&cnt==m)t=b[i],cnt=1;
else return -1;
}
return ans;
}
void mian(){
ll ans=0;
scanf("%lld%lld",&n,&m);
For(i,1,n){
scanf("%1lld",&a[i]);
}
ll t=a[1],cnt=1,tmp=0;//t为当前数,cnt为连续出现次数,tmp为第一个不满足好串的点
For(i,2,n){
if(a[i]==t&&cnt<m)++cnt;
else if(a[i]!=t&&cnt==m)t=a[i],cnt=1;
else{
tmp=i;
break;
}
}
if(tmp==0){//原串就是好的
printf("%lld\n",n);
return;
}
if(cnt!=m){//只能选择p=tmp-1
For(i,tmp,n)b[i-tmp+1]=a[i];
Rep(i,tmp-1,1)b[n-i+1]=a[i];
printf("%lld\n",check(tmp-1));
return;
}
ll tot=0;//统计结尾的连续长度
Rep(i,n,1){
if(a[i]==a[tmp-1])++tot;
else break;
}
if(tot>m){//已经超过了要求数量
printf("-1\n");
return;
}
tmp-=tot;//用m-tot个与结尾拼合
For(i,tmp,n)b[i-tmp+1]=a[i];
Rep(i,tmp-1,1)b[n-i+1]=a[i];
printf("%lld\n",check(tmp-1));
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
E.Manhattan Triangle
题目大意
对于两个点 ,曼哈顿距离为 。
给定平面上 个点 和一个整数 。定义三个点形成好的三角形当且仅当任意两点的曼哈顿距离都为 。
求出给定的点中的一个好的三角形。若存在,输出三个点的编号;若不存在,输出三个 。
多组测试,, 是偶数。
思路
曼哈顿距离有一个特点:到一个点的曼哈顿距离相同的点组成一个菱形。
根据这个特点可以发现,一个好的三角形,至少有一条过两个点的直线的倾斜角为 或 。
我们不妨枚举这条直线,从而确定两个点。
倾斜角 和 的情况可以分开考虑,只需要让所有点关于 轴对称后重新计算即可。
将处于同一条直线的点用 vector 记录下来,并按照 坐标排好序。
双指针找到曼哈顿距离为 的直线上的两点(它们的 坐标相差 ),然后在离这条直线水平距离为 的两条直线上分别二分找到三角形的第三个点。
复杂度 。
代码实现
注意下标加上一个较大的数防止变成负数。
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
#define pb push_back
const ll N=1e6+10;
using namespace std;
ll BIG=300000;//一个较大的数
ll n,k;
ll a[N],b[N];
ll m,f[N];
vector<ll>t[N];
struct node{
ll a,b,c;
}ans;
bool cmp(ll x,ll y){
return a[x]<a[y];
}
ll find(ll pos,ll x,ll y){//二分
if(pos<100000||pos>500000)return 0;//防止下标溢出
ll l=0,r=(ll)t[pos].size()-1,res=0;
while(l<=r){
ll mid=(l+r)>>1;
if(a[t[pos][mid]]>=x)res=t[pos][mid],r=mid-1;
else l=mid+1;
}
if(res&&a[res]<=y)return res;
else return 0;
}
void calc(){
m=0;
For(i,1,n){
f[++m]=a[i]-b[i]+BIG;
t[a[i]-b[i]+BIG].pb(i);
}
//离散化
sort(f+1,f+m+1);
m=unique(f+1,f+m+1)-f-1;
For(i,1,m)sort(t[f[i]].begin(),t[f[i]].end(),cmp);//按x坐标排序
For(i,1,m){
ll y=0;
For(x,0,(ll)t[f[i]].size()-1){
while(y<(ll)t[f[i]].size()&&a[t[f[i]][y]]-a[t[f[i]][x]]<k/2)++y;
if(y>=(ll)t[f[i]].size())break;
if(a[t[f[i]][y]]-a[t[f[i]][x]]==k/2){
ll t1=find(f[i]-k,a[t[f[i]][x]]*2-a[t[f[i]][y]],a[t[f[i]][x]]);
ll t2=find(f[i]+k,a[t[f[i]][y]],a[t[f[i]][y]]*2-a[t[f[i]][x]]);
if(t1)ans=(node){t[f[i]][x],t[f[i]][y],t1};
if(t2)ans=(node){t[f[i]][x],t[f[i]][y],t2};
}
}
}
For(i,1,m)t[f[i]].clear();
}
void mian(){
ans=(node){0,0,0};
scanf("%lld%lld",&n,&k);
For(i,1,n){
scanf("%lld%lld",&a[i],&b[i]);
}
calc();
For(i,1,n)a[i]=-a[i];
calc();
printf("%lld %lld %lld\n",ans.a,ans.b,ans.c);
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
F.Kostyanych's Theorem
题目大意
交互题。
交互库会有一个 个点的完全无向图,并从中删除恰好 条边。现在给定 ,你需要进行不超过 次以下询问:
-
"? ":交互库会找到编号最小的度数不小于 的节点 ,并找到编号最小的不与 直接相连的节点 。
-
如果 不存在,则返回 ;
-
如果 存在但 不存在,从图中删除点 ,返回 ;
-
否则从图中删除点 ,返回 。
-
你需要找到一条路径,经过原图上的每个点各一次。输出格式如下:
- "! ": 表示这条路径依次经过的点。
多组测试,。
思路
由于我们的查询次数有限,所以我们必须尽可能保证不做无效查询(返回 的查询)。
要做到这一点,我们需要知道图中节点的最大度数最小是多少。
我们知道图的边数是 。
假设每个节点的度数都为 ,那么图的边数为 。
当 时, 最大为 ,也就是说至少有一个节点的度数大于 ,也至少一个节点的度数小于等于 。
接下来考虑如何构造路径。
由于每次查询之后,交互库会将点 删除,考虑递归处理这个问题。边界条件为 或 。我们用双端队列记录路径。
尝试查询 。分类讨论。
-
:
没有度数为 的点,此时返回的 度数为 。
那么最优考虑下,我们找到一个度数最小的点 ,并将 加入路径。
也就是说我们再进行查询 即可。
但此时是不是变成了子问题呢?我们验证一下。
由于图中至少一个点的度数小于等于 ,点 度数一定小于等于 。
那么此时边数至少为 。
说明这是一个子问题。
-
:
返回的 度数为 。
此时我们知道 仅与 之间没有连边。
而路径有两个端点,我们将 插入到有连边的一端即可。
由于此时 ,递归返回的路径长度 ,所以端点必然不相同。
同样可以证明这是一个子问题。
最后输出路径就可以了。
代码实现
#include<bits/stdc++.h>
#define ll long long
#define For(i,a,b) for(ll i=(a);i<=(b);++i)
#define Rep(i,a,b) for(ll i=(a);i>=(b);--i)
const ll N=1e6+10;
using namespace std;
ll n;
deque<ll>ans;
pair<ll,ll> ask(ll d){//查询
printf("? %lld\n",d);fflush(stdout);
ll x,y;
scanf("%lld %lld",&x,&y);
return {x,y};
}
void calc(ll n){//递归处理
if(n==1){
ans.push_back(ask(0).first);
return;
}
if(n==2){
ans.push_back(ask(0).first);
ans.push_back(ask(0).first);
return;
}
pair<ll,ll>t=ask(n-2);
ll x=t.first,y=t.second;
if(y==0){
ll z=ask(0).first;
calc(n-2);
ans.push_front(x);
ans.push_front(z);
}else{
calc(n-1);
if(y==ans.front())ans.push_back(x);
else ans.push_front(x);
}
}
void mian(){
scanf("%lld",&n);
calc(n);
printf("! ");
while(ans.size()){
printf("%lld ",ans.front());
ans.pop_front();
}
printf("\n");
fflush(stdout);
}
int main(){
int T=1;
scanf("%d",&T);
while(T--)mian();
return 0;
}
尾声
如果有什么问题,可以直接评论!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!