2024.5.22晚训题解

我们的晚训难度更上一个层次了,其实仔细一想大家肯定都会做的。

从全球来看,2小时以内参赛并答题的选手,ABCDEFG题通过人数如图所示
你现在的实力在哪一档???

A题题解
太简单了。。。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d %d\n",min(x,y),max(x,y));
	}
	return 0;
}

B题题解
直接看看有没有一个位置相邻不同,交换即可

#include<bits/stdc++.h>
using namespace std;
char s[20];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%s",s+1);
		int n=strlen(s+1);
		bool ok=false;
		for(int i=2;i<=n;i++){
			if(s[i]!=s[i-1]){
				swap(s[i],s[i-1]);
				ok=true;
				break;
			}
		}
		if(ok)printf("YES\n%s\n",s+1);
		else printf("NO\n");
	}
	return 0;
}

C题题解
非常经典的判断区间相交问题,这个题的升级版本应该在atcoder出现过,给你N组这样子的线段,问有没有相交。其实就是判断区间相交,可以利用括号匹配的思路来做。先对所有区间端点排序,然后左端点看成L右端点看做R,括号匹配,如果此时的R匹配的是别人的L那么就有问题。

区间完全被包含或者独立,就不会有相交。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		int La=min(a,b);
		int Ra=max(a,b);
		int Lb=min(c,d);
		int Rb=max(c,d);
		if(Lb<La&&La<Rb&&Rb<Ra){
			printf("YES\n");
		}
		else if(La<Lb&&Lb<Ra&&Ra<Rb){
			printf("YES\n");
		}
		else{
			printf("NO\n");
		}				
	}
	return 0;
}


D题题解
其实你会发现连续的一段0 1 肯定是一起切的,先把数据简化,把连续的0 1压缩成单个1 0
然后你去找找规律,看看能发现啥? 10101010 0101010 要多少次

#include<bits/stdc++.h>
using namespace std;
char s[550];
char A[550];
int len=0;
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%s",s+1);
		len=0;
		int n=strlen(s+1);			
		for(int i=1;i<=n;i++){
			A[++len]=s[i];
			while(A[len]==s[i]&&i<=n)i++;
			i--;
		}
		if(len==1||len==2&&A[1]=='0'&&A[2]=='1'){
			printf("1\n");
		}
		else{
			if(len==2)printf("2\n");
		    else printf("%d\n",len-1);
		}
	}
	return 0;
}


E题题解
明白人一眼就能看出,Q次查询,每次问走了 D [ i ] D[i] D[i]要多少时间,其实就是二分,因为 D [ i ] D[i] D[i]可以处于某两个标记点中间,而每个标记点都有自己的到达时间。我们直接二分看看这个 D [ i ] D[i] D[i]在哪,用匀速运动算出那一小段时间,再加上前面的即可。问题就在于每段区间的匀速速度,这个东西你要仔细看看数据,就会发现可能达到很大很大的程度,而double无法承载这种精度,而且题目也说了向下取整

我们分析每次查询的答案无外乎是某一个标记点的时间+匀速运动一小段的时间
匀速运动的时间=一小段路程/(区间距离 除以 区间时间) 我们把除法转换一下,把分母的除法乘上去,变成 一小段路程*区间时间 除以 区间距离 这个公式就是我们的匀速运动的时间,不整除也没事,反正不整除的部分也不考虑,因为答案向下取整,所以本题算时间得这样子算,注意long long

#include<bits/stdc++.h>
using namespace std;
struct pe{
	long long int pos;
	long long int time;
	double v;
}A[100005];
int d;
bool check(int x,int y){
	if(A[x].pos>y)return true;
	else return false;
}
int Q[100005];
int main(){
	int t;
	scanf("%d",&t);
	A[0].pos=0;
	A[0].time=0;
	while(t--){
		int n,k,q;
		scanf("%d%d%d",&n,&k,&q);
		for(int i=1;i<=k;i++){
			scanf("%d",&A[i].pos);
		}
		for(int i=1;i<=k;i++){
			scanf("%d",&A[i].time);
			//A[i].v=(A[i].pos-A[i-1].pos)*1.0/(A[i].time-A[i-1].time);
			//之前我也是没注意到精度
		}
		A[k+1].pos=n+1;
		for(int i=1;i<=q;i++){
			scanf("%d",&Q[i]);
		}
		for(int i=1;i<=q;i++){
			
		int L=1,R=k+1;
		while(L<R){
			int mid=(L+R)/2;
			if(check(mid,Q[i])){
			  R=mid;	
			}
			else{
				L=mid+1;
			}
		}	
		printf("%lld ",A[R-1].time+ (long long int)((Q[i]-A[R-1].pos)*(A[R].time-A[R-1].time)/(A[R].pos-A[R-1].pos)));
		}
		printf("\n");
	}
	return 0;
}


F题题解
暴力枚举即可,根据四象限对称性质乘法即可

#include <bits/stdc++.h>
using namespace std;
int main() {
    int t;
    scanf("%d",&t);
    while (t--) {
    int  r;
    scanf("%d",&r);
    int total = 0;
    int  x = -r;
    int  y = 0;
    while (x != 0) {
        int  dist = x * x + y * y;
        dist = sqrt(dist);
        if (dist == r) {
            total++;
            y++;
        } else if (dist > r) {
            y--;
            x++;
        } else if (dist < r) {
            y++;
        }
    }
    cout << total * 4 << '\n';
    }
    return 0;
}

G题题解
还是异或运算,复习一遍,异或运算就是对两个数的二进制位操作,相同为0,不同为1
本题说两个数做异或<4才能交换位置,我们思考思考满足什么条件才能异或<4 ?
每个数的最低两位我们直接不考虑,我们考虑所有高位,但凡这两个数有一个高位不相同,那么异或的结果就是让该高位上的2进制的系数为1,因此异或结果绝对不可能<4

所以思路就很明确了: 对于除了前两位,其他2进制位完全相同的两个数字,才能进行交换位置
我们考虑标记,按二进制位进行映射标记,把除了前两位,其他位一样的数字标记到一起,他们内部可以任意交换,为了使得最终数组字典序最小,那肯定是小的摆前面去。
比如我举个例子,原数组里面1 3 5 7 9位置上面的 a b c d e数字满足可以任意交换,我们只需要把这5个数字按从小到大的顺序依次摆放在他们原本占据的位置上即可。

所以这个题我们需要标记,又因为不确定每种标记要放多少数字,所以vector就是很好的选择
标记map,以二进制串作为第一映射,vector作为第二映射
map<string,vector > 即我们把后缀一样的数字放在一起,注意我们还要标记这种后缀占据了哪些位置,因此需要两个标记。

#include<bits/stdc++.h>
using namespace std;
map<string,vector<int> >vel;
//后缀一样的放在一起,节点编号排序重新摆放 
map<string,vector<int> >pos;
int A[200005];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&A[i]);
			string s="";
			int now=A[i];
			A[i]=A[i]/2;
			A[i]=A[i]/2;//移除最低的两个位  
			for(int j=1;j<=30;j++){
				s+=('0'+A[i]%2);
				A[i]=A[i]/2;
			}
			vel[s].push_back(now);//不必反转s
			pos[s].push_back(i); 	
		}
		for(auto &Q: vel){
			sort(Q.second.begin(),Q.second.end());
			int len=Q.second.size();
			for(int i=0;i<len;i++){
				A[pos[Q.first][i]]=Q.second[i];
			}
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d ",A[i]);
		}
		printf("\n");
		vel.clear();
		pos.clear();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值