Atcoder Regular Contest 147

 

Atcoder Regular Contest 147

这是本蒟蒻第3次打的 \(ARC\),赛时的时候 \(B\) 题调代码调崩了, \(wa\) 了15发。所以来简略的写一下题解

A:

题目链接

题目大意:略
题目分析:

对于区间查找最大最小,为什么不用线段树呢?(可以直接排序,每次取第一个和最后一个即可 —— \(Zxilly\)

但是我们会发现对于修改的话,我们需要单点修改,而不知道下标,怎么解决呢?

我们可以用一个结构体记录下当前最大最小值的下标,可以方便以后的修改,具体实现看代码

代码实现:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 2e5 + 7;
struct T {
	int val , id;	
	
}maxx[M << 2];
struct TT {
	int val , id;
}minn[M << 2];
int a[M];
void change(int k) {
	if(maxx[k << 1].val > maxx[k << 1 | 1].val) maxx[k] = maxx[k << 1];
	else maxx[k] = maxx[k << 1 | 1];
	if(minn[k << 1].val < minn[k << 1 | 1].val) minn[k] = minn[k << 1];
	else minn[k] = minn[k << 1 | 1];
}
void buildtree(int k , int l , int r) {
	if(l == r) {
		maxx[k] = {a[l] , l};
		minn[k] = {a[l] , l};
		return;	
	}
	int mid = l + r >> 1;
	buildtree(k << 1 , l , mid);
	buildtree(k << 1 | 1, mid + 1  , r);
	change(k);
}
void change(int k , int l , int r , int p , int x) {
	if(l == r) {
		if(x == 0)
		minn[k].val = 0x7fffffff;//当 maxx = 0 时,minn 应变为极大值
		else minn[k].val =x;
		maxx[k].val = x;
		return;
	}
	int mid = l + r >> 1;
	if(p <= mid) change(k << 1 , l , mid , p , x);
	else change(k << 1 | 1, mid + 1 ,r  , p, x);
	change(k);
}
T query_max(int k , int l , int r , int L , int R) {
	if(L > r || R < l) return {0 , 0};
	if(L <= l && R >= r) return maxx[k];
	int mid = l + r >> 1;
	T ans = query_max(k << 1 , l , mid , L , R);
	T anss = query_max(k << 1 , mid + 1 , r , L , R);
	if(ans.val > anss.val)
	return ans;
	else return anss;
}	
TT query_min(int k , int l , int r , int L , int R) {
	if(L > r || R < l) return {0x3f3f3f3f , 0};
	if(L <= l && R >= r) return minn[k];
	int mid = l + r >> 1;
	TT ans = query_min(k << 1 , l , mid , L , R);
	TT anss = query_min(k << 1 , mid + 1 , r , L , R);
	if(ans.val < anss.val)
	return ans;
	else return anss;
}	
signed main () {
	ios::sync_with_stdio(0),cin.tie(0);
	int n; cin >> n;
	for(int i = 1; i <= n; ++ i) {cin >> a[i];}
	buildtree(1 , 1,  n);
	int cnt = 0;//用于记录删除个数
	int ans = 0;
	while(cnt < n - 1) {
		T x = query_max(1 , 1 , n , 1 , n);
		TT y = query_min(1 , 1 , n , 1 , n);
		int xx = x.val;
		int yy = y.val;
		ans ++;
		if(xx % yy == 0) {
			cnt ++;
			change(1 , 1, n ,x.id, 0);
			continue;
		}
		change(1 , 1 , n , x.id ,(xx % yy) );
	}
	cout << ans;
}

[========]

B:

题目链接

题目大意:略
题目分析:
  • [\(1\)] : 因为题目中说要满足操作 \(A\) 次数最少,所以我们可以确定 \(A\) 的次数是固定的,那么我们仔细观察一下 \(A\) 操作在干嘛,他将 \(p_i\)\(p_{i + 1}\) 进行了交换,也就是让他们所位于的位置的奇偶性发生了变化,那么这有什么用呢?我们再来观察 \(B\) 在干嘛? \(B\) 操作是将 \(p_i\)\(p_{i + 2}\) 进行了交换,我们知道对于一个数 \(x\)\(x\)\(x + 2\) 的奇偶性相同。

  • [\(2\)] : 有了以上的分析,我们大概就知道的 \(A\) 的重要性,那么我们先猜一下 \(A\) 的操作次数,假设 \((p_i \& 1) \neq (i \& 1)\) 的数量 \(num\) , 那么 \(A\) 的操作次数为 \(\frac{num}{2}\)

  • [\(3\)] : 那么下面我们来简单说明一下:因为操作 \(B\) 可以使奇偶性相同的下标的位置进行交换,那么我们只需要让 \((p_i \& 1) = (i \& 1)\) 即可.
    又因为只能用操作 \(A\) 来将 \((p_i \& 1) \neq (i \& 1)\) 变为该种情况,且如果存在一个 \((p_i \& 1) \neq (i \& 1)\) ,那么一定存在另一个 \(j\) , 满足 \((p_j \& 1) \neq (j \& 1)\),所以该种情况是成对出现的,所以我们只需要 \(\frac{num}{2}\) 次操作即可满足 : \(\forall i \in (1 , N)\) , \((p_i \& 1) = (i \& 1)\)

  • [\(4\)] : 那么满足 :\(\forall i \in (1 , N)\) , \((p_i \& 1) = (i \& 1)\) 之后 , 我们只需要将其从小到大排序即可

代码实现:

用两个 \(vector\) 来储存答案, 对于每次移动我们需要反向存答案,具体实现看代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int M = 5e2 + 20;
int p[M];
vector<char> ans;
vector<int> sum;
signed main () {
	ios::sync_with_stdio(0),cin.tie(0);
	int n; cin >> n;
	for(int i = 1; i <= n; ++ i) {cin >> p[i];}
	for(int i = 1; i < n; ++ i) {
		if((i & 1) != (p[i] & 1)) {
			int k;
			for(int j = i; j  < n; j += 2) {
				if((p[j] & 1) == (i & 1)) {//这里一定要注意顺序,可以用B交换一定先用B
					k = j;
					break;	
				}
				if((i & 1) == (p[j + 1] & 1)) {//这里注意下标
					ans.push_back('A');
					sum.push_back(j);	
					swap(p[j] , p[j + 1]);
					k = j;
					break;
				}
				
			}
			while(k > i) {//因为是从后往前交换的,所以从后往前进行操作B
			ans.push_back('B');
			sum.push_back(k - 2);//一定是k - 2,因为交换 p_i 与 p_{i + 2}
			swap(p[k] , p[k - 2]);
			k -= 2;
			}
		}
	}
	for(int i = 1; i <= n - 2; ++ i) {
		if(i == p[i]) continue;
		int k = i;
		for(int j = i + 2; j <= n; j += 2) {
			if(p[j] == i) {//找到大小为 i 的数
				k = j;
				break;	
			}
		}
		while(k > i) { //将 大小为 i 的 p_k 放到 i 位置上
			ans.push_back('B');
			sum.push_back(k - 2);
			swap(p[k] , p[k - 2]);
			k -= 2;
		}
	}
	cout << ans.size() << '\n';
	for(unsigned i = 0; i < ans.size(); ++ i) {//直接输出即可
		cout << ans[i] << ' ' << sum[i] << '\n';	
	}
	
}

由于笔者太弱了,所以只能写两道题,这次比赛还算成功吧。

[========]

完结散花

posted @ 2022-09-05 11:48  L3067545513  阅读(126)  评论(2编辑  收藏  举报