Codeforces Round #759 (Div. 2, based on Technocup 2022 Elimination Round 3)
这场时间太阴间了,十一点开场还延时两次,可怜早八人迟到;
不过昨天打的小号,今天算分的时候小号的分数
感谢codeforces在博主生日这天给予的浪漫QAQ(贴贴);
A. Life of a Flower
题目:——>传送门
题意:给一束花,初始的高度为1,之后过了n天,在这n天中,对于第i天如果浇水,则花高度加1,若i-1天也浇了水,则花的高度额外加4,当持续两天没有浇水,则花会枯萎此时高度为-1,给出每天的浇水状态,问第n天花的高度;
解题思路:数据范围不大,直接模拟得出最后的答案;
AC代码:
int a[105];
void solve(){
int n;
scanf("%d", &n);
for(int i = 1; i<=n; i++) scanf("%d", &a[i]);
int ans = 1;
int res = 0;
for (int i = 1; i <= n; i ++ ){
if(a[i] == 1){
if(a[i-1] == 1) ans+=5;
else ans+=1;
res = 0;
}
else res++;
if(res == 2){
ans = -1;
break;
}
}
cout<<ans<<endl;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
solve();
}
return 0;
}
B. Array Eversion
题目:——>传送门
题意:给出一个任意序列的数组,每次可以对于数组末尾的数字x对数组的顺序进行改变,规则为:对于最后一数x将小于等于x的数放到x的左边,大于x的数放到x的右边(注:各部分元素的顺序与操作前保持一致,例如原序列[4,2,1,5,3]会变成[2,1,3,4,5]),求需要经过几次才能使得序列无法做出改变;
解题思路:首先数组无法做出改变的终止条件就是当最后一个数为数组中最大的数,对于每一个x(x即当前数组最后的元素),每次改变都可以将x前连续的小于等于x的数移到左边,也就是说,下一个末尾的数是当前的x前第一个大于x的数,一次类推,直到末尾数为数组最大值;
AC代码:
const int N = 2e5+5;
int a[N];
void solve(){
int n;
scanf("%d", &n);
for(int i = 1; i<=n; i++) scanf("%d", &a[i]);
int ans = a[n];
int res = 0;
for(int i = n-1; i; i--){
if(a[i]>ans){
ans = a[i];
res++;
}
}
cout<<res<<endl;
return;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
solve();
}
return 0;
}
C. Minimize Distance
题目:——>传送门
题意:给出一个数组a,其中a[i]表示第i个仓库在数轴上的位置(包含负数,a[i]的值可能相同),每个仓库需要一袋产品,每次可以从数轴0的位置携带k袋产品前往各个仓库,问最少需要经历的路程为多少?(注:当走到最后一个仓库时不用返回原点);
解题思路:很明显的贪心题,贪心思路还是很好想的,将正数和负数分开考虑,再从大到小模拟需要走过的路程,最后减去正数和负数中绝对值最大的数(这个仓库为最后去的,不用返回);详情见代码注释;
AC代码:
typedef long long ll;
const int N = 2e5+5;
int a[N];
int b[N];
void solve(){
int n, k;
scanf("%d%d", &n, &k);
int x;
int cnt = 0, tot = 0;
for(int i = 1; i<=n; i++){
scanf("%d", &x);
if(x>0) a[++cnt] = x;//正数和负数分类讨论
if(x<0) b[++tot] = -x;
}
sort(a+1, a+cnt+1);sort(b+1, b+tot+1);//从小到大排序
ll ans = 0;
for(int i = cnt; i>=0; i-=k){//从最远的仓库开始,每次走过k个仓库,则下次去的仓库为第i-k远的仓库
ans+=(ll)a[i]*2ll;//路程来回,需要乘2;
}
for(int i = tot; i>=0; i-=k){//同上
ans+=(ll)b[i]*2ll;
}
ans-=max(a[cnt], b[tot]);//去所有仓库中最远的仓库的那一次运送所走的路程,该次可以不用回到原点;
cout<<ans<<endl;
return;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
solve();
}
return 0;
}
D. Yet Another Sorting Problem
题目:——>传送门
题意:给定一个任意序列的数组a,每次可以选择其中的三个位置i,j,k(1≤i,j,k≤n),令他们位置上的数发生传递周期改变 i→j→k→i ,将ai放在位置j,aj放在位置k,ak放在位置i,而不改变任何其他元素,问做出若干次这样的改变能否将数组变为不递减的顺序;
解题思路:考虑变化中逆序对数量的改变,首先对与不递减的数组逆序数量为0,而对于给定的操作,每次逆序对的变化都是偶数(证明如下);
证明:设a,b,c为其中改变的三个位置上的数;
设数组形式为:a……b……c,其中第一段……有n个数,第二段……有m个数;
此时做出改变后:c……a……b;
对于c:设第一段中有x个数大于c,第二段中有y个数大于c,两段中对于c的逆序对为(x+y)个,此时将c变到原来a的位置,则第一段中有n-x个数小于c,第二段中有m-y个数小于c,对于c的逆序对从(x+y)变成了(n-x+m-y),变化量为(n-2x+m-2y);
对于b:设第一段中有t个数大于b,第二段中有k个数小于b,两段中对于b的逆序对为(t+k)个,此时将b变到原来c的位置,则第一段中有t个数大于b, 第二段中有m-k个数大于c,对于b的逆序对数量从(t+k)变到(t+m-k),变化量为(m-2k);
对于a:设第一段中有p个数小于a,第二段中有q个数小于a,两段中对于b的逆序对为(p+q)个,此时将a变到原来b的位置,则第一段中有n-p个数大于a, 第二段中有q个数小于a,对于a的逆序对数量从(p+q)变到(n-p+q),变化量为(n-2p);
将上述变化量加起来为n-2x+m-2y+m-2k+n-2p = 2n-2m-2x-2y-2k-2p = 2(n-x-y-k-p);而对于a,b,c之间做周期改变,所以a,b,c之间逆序对数目不发生改变(除非其中有两个数相同),则逆序对变化量一定是偶数
则可以求出原序列的逆序对的奇偶性,看是否为偶排列,特别的如果序列中一个元素的个数超过了1,则可以通过周期改变将奇排列变为偶排列(这种情况也可以);
AC代码:
const int N = 5e5+5;
int a[N];
int tr[N*2];
int n;
//树状数组用于求逆序对的数量
int lowbit(int x){
return x&-x;
}
int sum(int n){
int ans = 0;
for(int i = n; i; i-=lowbit(i)) ans+=tr[i];
return ans;
}
void add(int x){
for(int i = x; i<=n; i+=lowbit(i)) tr[i]++;
}
void solve(){
scanf("%d", &n);
unordered_map<int, int> ma;
int ans = 0;
for(int i = 1; i<=n; i++) scanf("%d", &a[i]), ma[a[i]]++, ans = max(ans, ma[a[i]]);
if(ans>=2){
puts("YES");
return;
}
ans = 0;
for (int i = 1; i<=n; i++) {
ans += sum(n) - sum(a[i]);
add(a[i]);
}
if(ans%2 == 0) puts("YES");
else puts("NO");
for(int i = 1; i<=n+3; i++) tr[i] = 0;
return;
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
solve();
}
return 0;
}