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