两道 杂题

1、最少刷题数

小蓝老师教的编程课有 N 名学生,编号依次是 1...N。

第 i 号学生这学期刷题的数量是Ai。

对于每一名学生,请你计算他至少还要再刷多少道题,才能使得全班刷题比他多的学生数不超过刷题比他少的学生数。

输入格式

第一行包含一个正整数 N。

第二行包含 N 个整数:A1,A2,A3,...,AN。

输出格式

输出 N 个整数,依次表示第 1...N号学生分别至少还要再刷多少道题。

数据范围

对于 30%的数据,1≤N≤1000,0≤Ai≤1000。
对于 100% 的数据,1≤N≤100000,0≤Ai≤100000。

输入样例:
5
12 10 15 20 6
输出样例:
0 3 0 0 7

因为数据会有相等的,所以使用二分。

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define fp(i,a,b) for(int i=a;i<=b;++i)
const int N=1e6+10;
const int mod=1e9+7;
const double eps=1e-5;
typedef double db;
int n;
int mmax;
int a[N],b[N];
bool check(int x,int y)
{
	int st=-1;
	int ed=-1;
	
	int l=1,r=n;
	
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(b[mid]>=x)r=mid;
		else  l=mid+1;
	}
	st=l;
	
	l=1,r=n;
	while(l<r)
	{
		int mid=(l+r+1)>>1;
		if(b[mid]<=x)l=mid;
		else r=mid-1;
	}
	
	ed=l;
	
	int lcnt=st-1;
	int rcnt=n-ed;
	if(y<x)lcnt--;
	if(rcnt<=lcnt)return true;
	else return false;
	
}
signed main()
{
    cin>>n;
    
    fp(i,1,n)cin>>a[i],b[i]=a[i],mmax=max(mmax,a[i]);
    
    sort(b+1,b+1+n);
    
    for(int i=1;i<=n;i++)
    {
    	int l=a[i],r=mmax;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid,a[i]))r=mid;
    		else l=mid+1;
		}
		cout<<l-a[i]<<" ";
	}
 
    
	return 0;
} 


2、最大公约数

题目描述

给定一个数组,每次操作可以选择数组中任意两个相邻的元素 x, y并将其中的一个元素替换为gcd(x,y),其中 gcd(x,y) 表示 x 和 y 的最大公约数。请问最少需要多少次操作才能让整个数组只含 1。

输入格式

输入的第一行包含一个整数 n,表示数组长度。

第二行包含 n 个整数 a1​,a2​,…,an​,相邻两个整数之间用一个空格分隔。

输出格式

输出一行包含一个整数,表示最少操作次数。如果无论怎么操作都无法满足要求,输出 −1。

输入输出样例

输入 #1

3
4 6 9

输出 #1

4

说明/提示

【评测用例规模与约定】

  • 对于 30% 的评测用例,n≤500,ai​≤1000;
  • 对于 50% 的评测用例,n≤5000,ai​≤10^6;
  • 对于所有评测用例,1≤n≤10^5,1≤ai​≤10^9。

蓝桥杯 2022 国赛 A 组 D 题。

首先要使整个数列为 1,必须要先有一个位置的值为 1,再逐步拓展。问题就转化为拼出一个 1 的最少步数。又由于题目要求只能用相邻两数来进行gcd,所以要拼出 1 个 1 必须是一个区间内的数进行 gcd。维护一个区间 gcd,套个双指针就可以了。这里用的是 st 表,时间复杂度O(nlogn+n)。

 

 

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define fp(i,a,b) for(int i=a;i<=b;++i)
const int N=1e5+10;
const int mod=1e9+7;
const double eps=1e-5;
typedef double db;
int Max[N][25];
int lg[N];
int n;
int a[N];
int gcd(int a, int b) 
{
	return b ? gcd(b, a % b) : a;
}
int query(int l,int r)
{
	int k=lg[r-l+1];
	return gcd(Max[l][k],Max[r-(1<<k)+1][k]);
}
signed main()
{
	cin>>n;
	
	int cnt=0;
	
	fp(i,1,n)
	{
		cin>>Max[i][0];
		a[i]=Max[i][0];
		if(a[i]==1)cnt++;
	}
	
	for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
    for(int j=1;j<=lg[n];j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
    	{
    		Max[i][j]=gcd(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);
		}
	}
	
	if(cnt)
	{
		cout<<n-cnt;
		return 0;
	}
	if(query(1,n)>1)
	{
		cout<<-1;
		return 0;
	}
	
	
	int ans=n;
	
	for (int l=1,r=1;r<=n;r++) {
		while (l<r && query(l+1,r)==1) l++;
		if (query(l,r)==1) ans=min(ans,r-l);
	}
	cout<<n+ans-1;
 
    
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值