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';
}
}
由于笔者太弱了,所以只能写两道题,这次比赛还算成功吧。
[========]