Codeforces Round 890 (Div. 2) ABCD

A Tales of a Sort

题目大意:

给定一个长度为n的序列,需要将其构造成非严格递增序列,可进行的操作是对序列每个元素进行max(0,a[i] -1),问最少操作几次

思路:

一开始用的是二分答案,其实可以更简单的想就是找到逆序对的maxvalue即可

#include <bits/stdc++.h>

const int MAX_N = 1000020;
const long long int inf = 10000000; 
#define int long long
#define int64_MAX 9223372036854775807 
const int MOD =  998244353;
using namespace std;
typedef pair<double,double>P;//first b,second, c;
//1 0 1 0 1
bool check(vector<int>arr,int op){
	for(int j=0;j<arr.size();j++){
		arr[j] = max(1LL*0,arr[j]-op);
	}
	int pre=-1;
	for(int i=0;i<arr.size();i++){
		if(pre!=-1 && arr[i]-pre<0){
			return false;
		}
		pre=arr[i];
	}
	return true;
}

int solve(){
	int n;
	cin>>n;
	vector<int>arr(n);
	int pre=-1;
	bool flag=false;
	int maxn=0;
	for(int i=0;i<n;i++){
		cin>>arr[i];
		maxn = max(maxn,arr[i]); 
		if(pre!=-1 && arr[i]-pre<0){
			flag=true;
		}
		pre=arr[i];
	}
	if(!flag)return 0;//本身就是升序
	//二分答案 答案区间范围0 - maxn 因为最多都变成0
	int l = 0,r = maxn;
	while(r>l){
		int mid = (l+r)>>1;
		if(check(arr,mid)){
		//	cout<<mid<<"\n";
			r=mid;
		}else{
			l=mid+1;
		}
	}
	return l;
}

signed main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	//freopen("tot.txt","w",stdout) ;
	int t=1;	
	cin>>t;
	while(t--){
		cout<<solve()<<"\n";
	}
	return 0;
}

 B. Good Arrays

题目大意:

同样是给定一个长度为n的序列,问能不能构造出新的序列使得满足俩个条件
1、新的序列所有项和等于原序列和

2、新的序列下标对应的原序列值不能相同

思路:

从至少的可能性出发要保证能构造出满足条件的b序列

首先对于原序列中值为1的项,在新序列中必然需要大于1即增加

而其他项则可选择增加or减小

那么就从该点出发

统计一遍count_not_1 、 count_1  、 sum_not_1 

构造出新序列需要满足的前提条件是
sum_not_1 - count_not_1 >= 2*count_1

//即最坏情况 将原序列非1的项元素全部减小为1 ,此时可分配的数值有

sum_not_1 - count_not_1 ,将其均匀分配在原序列为1的项位置上至少为2即可  

#include<bits/stdc++.h> 
const int MAX_N = 1e9+10;
#define int long long
using namespace std;

bool solve(){
	int n,x;
	cin>>n;
	int sum=0,count_not_1=0,count_1=0;
	for(int i=0;i<n;i++){
		cin>>x;
		if(x==1) ++count_1;
		else{
			++count_not_1;
			sum+=x;
		} 
	}
	if(n==1)return false;
	return sum-count_not_1+count_1>=2*count_1;
}


signed main( )
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	
	int t=1;
	cin>>t;
	while(t--){
		cout<<(solve()?"YES\n":"NO\n");
	}
    return 0;
}

 C. To Become Max

题目大意:

给定一长度为n的序列,以及可操作次数k,问在有限操作次数内构造出最大值的大小

操作:

在正序对中左端点进行+1操作,右端点不变

思路:

标志性的样例:

5 6

6 5 4 1 5

实现操作:

将1升至4 、 4升至5 、5升至6 、 6升至7

ans 即为7

即会发现假设答案为h

那么有一段序列一定满足

h 、h-1 、h-2、h-3……

#include<bits/stdc++.h> 
const int MAX_N = 1e9;
const int N = 1e3+100;
#define int long long
using namespace std;
int n,k;
int a[N];
bool check(int u,int h){//当前位置以及枚举的答案 
	int cnt = k;//操作次数;
	//尝试构造在有限操作次数内满足h、h-1、h-2、h-3…………
	for( int i=u; i<n ; i++,h--){
		if(h <= a[i])return true;
		cnt -= h - a[i];//如果此时的a[i]小于此时的h 那么可以尝试通过有限次数内增加
		if(cnt < 0)return false; //操作次数不够了 
	} 
	return false;
}

int solve(){
	cin>>n>>k;
	memset(a,0,sizeof(a));//初始化a数组为0 
	int ans =0;
	for(int i=0;i<n;i++) {
		cin>>a[i];
		ans = max(ans, a[i]);//原状态数组最大值 
	}
	
	//枚举出首位置为开头 
	for(int i=0;i<n;i++){
		//二分答案 
		int l =1, r= MAX_N;
		while(l<=r){
			int mid = (l+r)>>1;
			if(!check(i,mid))r= mid-1;//二分的答案太大了 
			else l=mid+1;
		}
		ans = max(ans, r);
	}
	return ans;
}


signed main( )
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	
	int t=1;
	cin>>t;
	while(t--){
		cout<<solve()<<"\n";
	}
    return 0;
}

 D. More Wrong

题目大意:

交互题

询问区间 返回逆序对个数,问隐藏序列的最大值位置

思路:

假设知道左右区间最大值分别为x,y

再进行询问区间x,y的逆序对个数 、 x+1,y的逆序对个数分别为a,b

如果说

a == b+y-x 那么该区间最大值则为x 否则为y

依据:

假设该区间的最大值为a 则对答案的贡献是y-x

 

#include<bits/stdc++.h> 
const int MAX_N = 1e9;
const int N = 1e3+100;
#define int long long
using namespace std;
int n,tot;
int get(int l,int r){
	if(l==r)return l;
	int mid = (l+r)>>1;
	int a = get(l,mid),b = get(mid+1,r);
	cout<<"? "<<a<<" "<<b <<endl;
	cin>>tot;
	int till;
	//在询问一次[a+1,b]区间 
	if(a+1 == b)till=0;//区间元素为1 
	else{
		cout<<"? "<<a+1<<" "<<b<<endl;
		cin>>till; 
	}
	return tot==till+b-a?a:b;
}
void solve(){
	cin>>n;
	int ans = get(1,n);
	cout<<"! "<<ans<<endl;
}
signed main( )
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int t=1;
	cin>>t;
	while(t--){
		solve(); 
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenRenning

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值