CodeForces Round 669A-D题解

本文精选四道算法竞赛题目,包括字符串操作、序列排列、交互式问题解决及路径寻优,详细解析题意与解题思路,涵盖单调栈、GCD、字符串匹配等算法技巧。

A:Ahahahahahahahaha
题意:给定一个01串,01串的长度保证为偶数,问删除最多n/2个元素能否使得奇数位和等于偶数位置和
题解:首先很明显0对于和是没有影响的,所以在原串中0的数量>=n/2那我们就直接输出n/2个0即可,问题就在于1的数量>n/2的时候怎么办。这时若(n/2)%2=0那我们同理输出n/2个1,若(n/2)%2=1,那就输出(n/2)+1个1(因为此时保证1的数量>(n/2))

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll rd(){
	ll x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	return x*f;
}
const int maxn=1e3+5;
const ll mod=998244353;
ll ksm(ll a,ll b){ll ans=1;while(b){if(b&1)ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll gcd(ll a, ll b){ll t;while(b){t=b;b=a%b;a=t;}return a;}
int _;
int n,a[maxn];
int main() {
    for(scanf("%d",&_);_;_--){
        n=rd();
        for (int i=1;i<=n;i++)a[i]=rd();
        int ji=0,ou=0;
    	for (int i=1;i<=n;i++){
    		if (a[i]==1)ji++;
    		else ou++;
    	}
    	if (ou>=n/2){
    		printf("%d\n",n/2);
    		for (int i=1;i<=n/2;i++)
    			printf("0 ");
    	}
    	else{
    		if ((n/2)%2==0){
    			printf("%d\n",n/2);
    		for (int i=1;i<=n/2;i++)
    			printf("1 ");
    		}
    		else{
    			printf("%d\n",n/2+1);
    			for (int i=1;i<=n/2+1;i++)
    			printf("1 ");
    		}
    	}
    	puts("");
    }
    return 0;
}

B:Big Vova
题意:给定一个序列,定义c[i]为gcd(b[1]…b[i]),将序列合理排列成b,使得c字典序最大
题解:数据范围就挺明显的 1e3直接n2暴力就可,每次c[i]相当于对于上一次结果和剩下的所有数都尝试做一次gcd把最大的拿过来就行

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll rd(){
	ll x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	return x*f;
}
const int maxn=1e3+5;
const ll mod=998244353;
ll ksm(ll a,ll b){ll ans=1;while(b){if(b&1)ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
int gcd(int a, int b){int t;while(b){t=b;b=a%b;a=t;}return a;}
int _;
int n,a[maxn],vis[maxn],ans[maxn];
int main() {
    for(scanf("%d",&_);_;_--){
        n=rd();int mx=0;
        for (int i=1;i<=n;i++){
        	a[i]=rd();vis[i]=0;
        	if (a[i]>a[mx])mx=i;
        }
        int now=2,nowx=a[mx];
        vis[mx]=1;
        ans[1]=a[mx];
        for (;now<=n;now++){
        	int tmp=1,p=0;
        	for (int i=1;i<=n;i++){
        		if (vis[i])continue;
        		if (gcd(nowx,a[i])>=tmp){
        			p=i;tmp=gcd(nowx,a[i]);
        		}
        	}
        	ans[now]=a[p];
        	vis[p]=1;
        	nowx=tmp;
        }
        for (int i=1;i<=n;i++)
        	printf("%d ",ans[i]);
        cout<<endl;
    }
    return 0;
}

C:Chocolate Bunny
题解:交互题,有一个长度为n的排列,你最多可以询问2n次a[x]%a[y]的值,然后给出序列的全貌。
题解:首先这题我们需要知道一个小技巧,对于x和y两个不同的数,我们只需要两次询问一定可以得到其中一个数。假设x<y, x%y=x,y%x<x,反之同理,所以我们对整个序列一直做反复的操作可以保证在2n次内得到全部值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll rd(){
	ll x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	return x*f;
}
const int maxn=1e4+5;
int n;
int vis[maxn],ans[maxn];
map<int,map<int,int> >v;//赛中代码写的很丑 其实可以有很多更短的写法
int main() {
    n=rd();
    if (n==1){
    	printf("! 1\n");
    	cout.flush();
    }
    int s=1,e=2;
   	for (int now=1;now<n;now++){
   		printf("? %d %d\n",s,e);
   		fflush(stdout);
   		scanf("%d",&v[s][e]);
   		printf("? %d %d\n",e,s);
   		fflush(stdout);
   		scanf("%d",&v[e][s]);
   		if (v[e][s]>v[s][e]){
   			ans[e]=v[e][s];
   			vis[v[e][s]]=1;
   			e++;
   		}
   		else{
   			ans[s]=v[s][e];
   			vis[v[s][e]]=1;
   			s=e;
   			e++;
   		}
   	}
   	int p=0;
   	for (int i=1;i<=n;i++)
   		if (vis[i]==0)p=i;
   	if (ans[s]==0)
   		ans[s]=p;
   	else
   		ans[e]=p;
   	printf("!");
   	for (int i=1;i<=n;i++)printf(" %d",ans[i]);
   	printf("\n");
   	fflush(stdout);
    return 0;
}

D:Discrete Centrifugal Jumps
题意:给定n个高楼的高度,要从1跳到n,问最少跳的步数。
跳法定义为从i到j需满足以下任意一个条件
1:j=I+1.
2:i+1到j-1的高楼全部严格小于i和j的高度
3i+1到j-1的高楼全部严格高于i和j的高度
题解:
条件3与2类似,我们这里就讨论2怎么办。我们开一个单调栈,单调栈的模版就是寻找i后面第一个大于i的下标,那其实变相就是这题i可以跳到j 当然还有i-j之间可能出现k,a[i]=a[k]那就不满足了,这个我们只需要在单调栈处理的时候,每次push之前都判断一下栈顶元素是否相等当前元素,如果相等就pop出去。此题跟单调栈模版题有一点不一样的是,你pop掉最后一个元素之后,是可以从栈顶元素跳过来的,因为相当于当前这个元素是小于栈顶元素里最大的,所以满足条件2.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
inline ll rd(){
	ll x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	return x*f;
}
const int maxn=3e5+5;
int n,a[maxn],dp[maxn];
stack<int>s1,s2;
int main() {
    n=rd();
    for (int i=1;i<=n;i++)a[i]=rd(),dp[i]=inf;
    dp[1]=0;
	s1.push(1);s2.push(1);
	for (int i=2;i<=n;i++){
		while (!s1.empty()&&a[s1.top()]<a[i]){
			dp[i]=min(dp[i],dp[s1.top()]+1);
			s1.pop();
		}
		if (!s1.empty()){
			dp[i]=min(dp[i],dp[s1.top()]+1);
			if (a[s1.top()]==a[i])s1.pop();
		}
		s1.push(i);
		while (!s2.empty()&&a[s2.top()]>a[i]){
			dp[i]=min(dp[i],dp[s2.top()]+1);
			s2.pop();
		}
		if (!s2.empty()){
			dp[i]=min(dp[i],dp[s2.top()]+1);
			if (a[s2.top()]==a[i])s2.pop();
		}
		s2.push(i);
	}
	cout<<dp[n]<<endl;
    return 0;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值