Codeforces Deltix Round Summer 2021 [Div.1 + Div.2]个人题解

Codeforces Deltix Round Summer 2021 [Div.1 + Div.2]个人题解

比赛链接:Codeforces Deltix Round Summer 2021 [Div.1 + Div.2]

A题 A Variety of Operations

题目大意:

给你两个数 \(a\)\(b\) ,初始值都为 \(0\),你有三种操作:\(1.(a+k,b+k)\) \(2.(a+k,b-k)\) \(3.(a-k,b+k)\)
给出 \(c,d\) 问最少的操作次数使 \(a=c\)\(b=d\)

思路解析:

我们可以直观地发现三种操作是不会改变 \(a-b\) 的奇偶性的,所以如果 \(c-d\) 为奇数的话就一定不可能有可行方案的。

如果 \(a-b\) 为偶数的话,我们可以发现我们可以用不超过 \(2\) 次操作使它由 \(a,b\)\(c,d\) ,第一步 \((a+(a+b)/2,b+(a+b)/2)\) ,第二步 \((a+(a-b)/2,b-(a-b)/2)\)\((a-(a-b)/2,b+(a-b)/2)\)

特别的有两种特殊情况需要考虑,如果 \(c,d\) 都为 \(0\) 的时候,不需要进行任何操作,如果 \(c=d\) 的时候,我们只需进行一次操作一即可

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

int main(){

	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);

	int t;
	cin>>t;
	while(t--){
		int  n,m;
		cin>>n>>m;

		if(n==0&&m==0){
			cout<<0<<endl;
			continue;
		}

		if((abs(n-m))%2==1)cout<<-1<<endl;
		else if(n==m)cout<<1<<endl;
		else cout<<2<<endl;
	}

}

B题 Take Your Places!

题目大意:

给出一数组,你可以每次交换相邻的两个元素,问最少多少次能够把数组变成奇偶相间,如果不能输出 \(-1\)

思路解析:

首先我们考虑不能的情况:很显然,如果 \(abs(奇数数量-偶数数量)> 1\) 一定不能

如果可行的话,怎样使次数最少呢?

我们可以把交换次数理解为当前元素与它目标位置的距离,因为我们可以通过他们之间距离次交换来交换任意两个元素的位置,所以我们只要把应该交换的元素移到他最近的目标位置即可。

我们可以这样考虑,什么情况下满足最终的要求,只有两种情况:奇偶奇偶奇偶... 或者 偶奇偶奇偶奇...
所以我们就可以单独存储奇数和偶数,按下标大小从小到大,一一对应目标位置的奇偶,答案就只需累加他与目标位置之间的距离即可,当然还需要取 \(min\)

其实,如果 \(abs(奇数数量-偶数数量) = 1\) 的话,可以发现他的目标位置是唯一的

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;

ll a[maxn],pos[maxn],top;
ll pos2[maxn];
ll sum,tot;

int main(){

	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);

	int t;
	cin>>t;
	while(t--){
		tot=0;
		sum=0;
		ll n;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			if(a[i]%2==0)pos[++sum]=i;
			else pos2[++tot]=i;
		}
		if(abs(tot-sum)>1)cout<<-1<<endl;
		else {
			ll ans=0,ans2=0;
			ll now=1;
			for(int i=1;i<=sum;i++){
				if(pos[i]!=now)ans+=abs(now-pos[i]);
				now+=2;
			}
			now=1;
			for(int i=1;i<=tot;i++){
				if(pos2[i]!=now)ans2+=abs(now-pos2[i]);
				now+=2;
			}
			if(sum==tot)cout<<min(ans,ans2)<<endl;
			else if(sum>tot)cout<<ans<<endl;
			else cout<<ans2<<endl;
		}
	}
}

C题 Compressed Bracket Sequence

题目大意:

给你一个括号字符串的映射数组 \(a\) ,奇数位表示左括号的数量,偶数位表示右括号的数量,这样形成了一个长度为 \(\sum a_i\) 的括号字符串,问有多少合法括号序列子串

思路解析:

我们可以从前往后枚举左括号,来计算每种左括号对应的情况数,毕竟每个合法子序列都是以左括号开头的。

那么我们该如何计算呢?

我们对于每组左括号,向右遍历,如果是左括号,我们就把他加到额外的左括号里(注意这个额外的并非是我们枚举的那个左括号),对于右括号,我们优先把他与额外的左括号匹配,不加入答案,因为在后面我们还会枚举到这个右括号的上一个左括号,避免重复计算,如果匹配完后右括号还有剩余,就和枚举的左括号匹配,加入答案,当右括号此时还有剩余,那么就退出当前左括号遍历,否则继续向下匹配知道把左括号全部匹配掉。

另外,对于每种情况,我们还需要加上好几个合法已匹配的括号序列并排的情况,判断一下就好

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=1e5+5;

ll a[maxn],ans;

int main(){
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);

    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];

    for(int i=1;i<=n;i+=2){
        ll top=a[i],m=0;
        for(int j=i+1;j<=n;j++){
            if(j%2==1){
                m+=a[j];
            }
            else {
                ll sum=a[j];
                ll p=min(sum,m);
                sum-=p;m-=p;
                if(m==0){
                    p=min(top,sum);
                    ans+=p+(j>i+1);
                    top-=p;sum-=p;
                    if(sum)break;
                }
            }
        }
    }

    cout<<ans<<endl;

}
posted @ 2021-08-31 19:47  不会飞的小飞龙  阅读(52)  评论(0编辑  收藏  举报
Live2D