比赛链接:https://codeforces.com/contest/1746
A:
题意:给定一个长度为N的数组,将其分为K个区间,在每一个区间里,取一个Max或者是Min代表该区间,问最后这些区间能否通过这两种操作变为1。
方法:观察规律。如果长度为N的数组中,出现了1,这无论如何,Min=1,其他区间随便取Max或者Min,最终答案都是1。否则就不能变为1。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,k;
int a[N];
inline void solve(){
memset(a,0,sizeof(a));
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(a[i]==1){
cout<<"YES\n";return;
}
}
cout<<"NO\n";
}
int main(){
int T;cin>>T;
while(T--) solve();
}
B:
题意:给定一个长度为N的01数组,通过操作 将该数组变为(非严格)递增数组,求最少的操作次数。
方法:模拟匹配
举样例4进行说明:ans=3
具体看代码讲解。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
int a[N];
inline void solve(){
cin>>n;
vector<int>b,c;
for(int i=1;i<=n;i++){
cin>>a[i];
//再输入数组的同时,将1,0的下标分别放入b,c寄存器中
if(a[i]==1) b.push_back(i);
else c.push_back(i);
}
int ans=0;
//双指针,i为1的位置从前往后扫,j为0的位置从后往前扫
for(int i=0,j=c.size()-1;;i++,j--){
if(i>=b.size()||j<0) break;//越界
if(b[i]>c[j]) break;//指针相遇
ans++;
}
cout<<ans<<"\n";
}
int main(){
int T;cin>>T;
while(T--) solve();
}
C:
题意:给定一个长度为N的数组,进行i次操作,第i次操作选择一个数的后缀,并且这数的后缀以及本身都会+i,问我们如何执行操作以最小化最终数组中的反转次数。(使逆序对数最少)
方法:一个数组,经过区间操作,一定可以将整个数组的逆序对数化为0。若一个数组存在逆序对的话,则该数组的差分数组一定会有负数的存在,我们知道,给一定区间的数同时加一个相同的数,差分数组是不会变的。
举样例2:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
int ans[N],a[N],d[N];//ans数组记录第i次操作所需要选择的后缀
inline void solve(){
cin>>n;
set<int>e;
for(int i=1;i<=n;i++) e.insert(i),ans[i]=n;
for(int i=1;i<=n;i++){
cin>>a[i];
d[i]=a[i]-a[i-1];//原数组中第i位的差分值
if(d[i]<0){
int x=*e.lower_bound(-d[i]);//找到第一个大于等于(差分值)的数
e.erase(x);//在容器中消除这个值:因为每个操作只能进行一次
ans[x]=i;//第x操作所需要选择的数i的后缀
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<"\n";
}
int main(){
int T;cin>>T;
while(T--) solve();
}