Codeforces Round 951 (Div. 2) 题解

本文网址:https://www.cnblogs.com/zsc985246/p/18236377 ,转载请注明出处。

好久没更新了,诈尸一下。

挑战最快题解。

B 题刚做完的时候网络爆炸了,掉了一点分。最终排名 22。

题目做起来很爽。大爱思维与构造!抵制数据结构!

2024/6/7update:修改了一些抽风表述(原谅赛时脑子不好使)。

2024/6/9update:更新 F 题解。

传送门

Codeforces Round 951 (Div. 2)

A.Guess the Maximum

题目大意

给定一个长度为 n 的数组 a,求一个最大的数 x,使得任意一个长度不小于 2 的连续子串的最大值大于 x

多组测试,2n5×104,1ai109,T104,n5×104

思路

最大值尽可能小,那么选择的连续子串越短越优。

枚举所有长度为 2 的连续子串,求出它们最大值的最小值,然后减去 1 输出即可。

代码实现

#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

题目大意

给定两个数 x,y,构造无限长度序列 an=nx,bn=ny。求两个序列的最长公共子序列长度。

多组测试,0x,y109,xy,T104

思路

考虑公共子序列如何形成。

假设现在满足 xn=ym。如果想要 n,m 自增之后仍然满足条件,那么在自增过程中,n,m 的每一个二进制位要么同时改变,要么同时不变

然后考虑 n,m 的一个满足条件的解:n=x,m=y。我们将 n,m 所有相同的位全部变为 0,得到 n,m 的最小解。

此时 n,m 能够自增的次数显然是最多的。如果此时 n,m 的二进制低位连续的 0 的个数为 t1,t2,那么答案就是 2min{t1,t2}

也就是说,令 x,y 从最低位开始的连续相同二进制位的个数为 t,那么答案即为 2t

代码实现

#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

题目大意

给定一个长度为 n 的序列 a,你需要构造一个长度也为 n 的序列 b,满足 i,aibi>j=1nbj

无解输出 1

多组测试,1n50,2ai20,T104,n2×105

思路

显然让所有的 aibi 都相等时最优。

求出 ai 的最大公倍数,然后计算验证是否合法即可。

代码实现

#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

题目大意

给定一个长度为 n 的 01 串 s 和一个整数 k,你需要恰好进行一次操作:

  • 选择一个 1pn,将序列变为 sp+1sp+2snspsp1s1

定义一个 01 串 s 是好的当且仅当满足以下条件:

  • s1=s2==sk

  • 1ink,sisi+k

求是否能将 s 变为好串。如果可以,输出 p,否则输出 1

多组测试,2n105,1kn,T104,n2×105nk 的倍数

思路

发现操作后 s1 总在最后,结合序列的最终条件,考虑从这里入手。

s[l,r] 表示 slsl+1sr

为了满足序列的格式,s[1,pk×k] 以及 s[p+1,npk×k+p] 必定是好的 01 串,因为这些地方是操作无法影响到的。

分类讨论。

从前往后看,找到第一个使好串条件不成立的位置 tmpstmp1t

  • tmp1(modk),此时的连续 t 的个数没有到达 k 个,从中间划开必定不符合条件,只能选择 p=tmp1

  • tmp1(modk),此时连续 t 的个数超过 k 个,必须从中间划开,所以求出 s 结尾的连续 t 个数 tot,然后选择 p=tmptot1

代码实现

#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

题目大意

对于两个点 (x1,y1),(x2,y2),曼哈顿距离为 |x1x2|+|y1y2|

给定平面上 n 个点 (xi,yi) 和一个整数 k。定义三个点形成好的三角形当且仅当任意两点的曼哈顿距离都为 k

求出给定的点中的一个好的三角形。若存在,输出三个点的编号;若不存在,输出三个 0

多组测试,3n105,2k4×105,105xi,yi105,T104,n2×105k 是偶数

思路

曼哈顿距离有一个特点:到一个点的曼哈顿距离相同的点组成一个菱形。

根据这个特点可以发现,一个好的三角形,至少有一条过两个点的直线的倾斜角为 45135

我们不妨枚举这条直线,从而确定两个点。

倾斜角 45135 的情况可以分开考虑,只需要让所有点关于 y 轴对称后重新计算即可。

将处于同一条直线的点用 vector 记录下来,并按照 x 坐标排好序。

双指针找到曼哈顿距离为 k 的直线上的两点(它们的 x 坐标相差 k2),然后在离这条直线水平距离k 的两条直线上分别二分找到三角形的第三个点。

复杂度 O(nlogn)

代码实现

注意下标加上一个较大的数防止变成负数。

#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

题目大意

交互题。

交互库会有一个 n 个点的完全无向图,并从中删除恰好 n2 条边。现在给定 n,你需要进行不超过 n 次以下询问:

  • "? d":交互库会找到编号最小的度数不小于 d 的节点 x,并找到编号最小的不与 x 直接相连的节点 y

    • 如果 x 不存在,则返回 0 0

    • 如果 x 存在但 y 不存在,从图中删除点 x,返回 x 0

    • 否则从图中删除点 x,返回 x y

你需要找到一条路径,经过原图上的每个点各一次。输出格式如下:

  • "! x1 x2 xn":xi 表示这条路径依次经过的点。

多组测试,2n105,T104,n105

思路

由于我们的查询次数有限,所以我们必须尽可能保证不做无效查询(返回 0 0 的查询)。

要做到这一点,我们需要知道图中节点的最大度数最小是多少

我们知道图的边数是 m=n(n1)2(n2)=n23n+42

假设每个节点的度数都为 t,那么图的边数为 m1=nt2

m1<m 时,t 最大为 n3,也就是说至少有一个节点的度数大于 n3,也至少一个节点的度数小于等于 n3


接下来考虑如何构造路径。

由于每次查询之后,交互库会将点 x 删除,考虑递归处理这个问题。边界条件为 n=1n=2。我们用双端队列记录路径。

尝试查询 d=n2。分类讨论。

  • y=0

    没有度数为 n2 的点,此时返回的 x 度数为 n1

    那么最优考虑下,我们找到一个度数最小的点 z,并将 zx 加入路径。

    也就是说我们再进行查询 d=0 即可。

    但此时是不是变成了子问题呢?我们验证一下。

    由于图中至少一个点的度数小于等于 n3,点 z 度数一定小于等于 n3

    那么此时边数至少为 m=m(n1)(n3)+1=n27n+142=(n2)23(n2)+42

    说明这是一个子问题。

  • y0

    返回的 x 度数为 n2

    此时我们知道 x 仅与 y 之间没有连边。

    而路径有两个端点,我们将 x 插入到有连边的一端即可。

    由于此时 n3,递归返回的路径长度 l2,所以端点必然不相同。

    同样可以证明这是一个子问题。

最后输出路径就可以了。

代码实现

#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;
}

尾声

如果有什么问题,可以直接评论!

posted @   zsc985246  阅读(1279)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示