1 AC情况
A | B | C | D | E | F | G1 | G2 |
---|---|---|---|---|---|---|---|
Accepted \color{green}\texttt{Accepted} Accepted | Accepted \color{green}\texttt{Accepted} Accepted | Accepted \color{green}\texttt{Accepted} Accepted | Accepted \color{green}\texttt{Accepted} Accepted | Accepted \color{green}\texttt{Accepted} Accepted | - | - | - |
2 题解
A. Morning
题目描述
输入 t t t 组数据。
每组数据有一个四位密码。现在密码输入设备上有一个数列
1234567890
,初始时光标指向1
。在 1 1 1 秒内,你可以:
- 按下光标以显示当前数字;
- 将光标移动到任何相邻的数字。
请计算输入给定的四位密码所需的最少秒数。
题解
模拟即可,注意把0
看做10
来处理,这样从数字
i
i
i 移动到数字
j
j
j 所需的秒数就是
∣
i
−
j
∣
\left|i - j\right|
∣i−j∣,最后再加上显示数字的时间
4
4
4 即可。
代码如下:
#include <bits/stdc++.h>
#define endl '\n'
#define file(FILENAME) freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout)
#define CLOSE ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, t;
int main() {
CLOSE;
cin >> t;
while (t--) {
cin >> n;
int a = n / 1000, b = n / 100 % 10, c = n / 10 % 10, d = n % 10; //拆分数字
if (a == 0) a = 10; //10替换0
if (b == 0) b = 10;
if (c == 0) c = 10;
if (d == 0) d = 10;
int ans = 4 + abs(a - 1) + abs(b - a) + abs(c - b) + abs(d - c); //移动时间+显示时间
cout << ans << endl;
}
return 0;
}
B. Chemistry
题目描述
输入 t t t 组数据。
每次输入 n n n 和 k k k,和一个仅包含小写字母的字符串 s s s( ∣ s ∣ = n \left|s\right| = n ∣s∣=n)。问在 s s s 中删除 k ( 0 ≤ k < n ) k(0\le k < n) k(0≤k<n) 个字符后,将 s s s 重新排列,能否构成一个回文串。
题解
回文串的特征:
- 最多只存在一个字母出现次数为奇数;
- 其余字母出现次数均为偶数。
我们可以从出现奇数次的字母入手,如果出现奇数次,删去一个,使它成为偶数;不能删了,就记录下奇数的个数。
如果最后出现奇数次的字母的个数大于 1 1 1,那么一定不是回文串。
代码如下:
#include <bits/stdc++.h>
#define endl '\n'
#define file(FILENAME) \
freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout)
#define CLOSE \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, k, t, cnt[N];
char s[N];
int main() {
CLOSE;
cin >> t;
while (t--) {
memset(cnt, 0, sizeof cnt);
cin >> n >> k;
cin >> s;
for (int i = 0; i < n; i++) //记录出现次数
cnt[s[i]]++;
int flag = 0;
for (int i = 'a'; i <= 'z'; i++) {
if (cnt[i] % 2 != 0) { //出现奇数次
if (k) { //能删则删
k--;
cnt[i]--;
} else //不能删,记录个数
flag++;
}
}
cout << (flag <= 1 ? "YES\n" : "NO\n");
}
return 0;
}
C. Raspberries
题目描述
输入 t t t 组数据。
每次给定数组 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an 和一个数 k ( 2 ≤ k ≤ 5 ) k(2\le k\le 5) k(2≤k≤5)。
每次操作你可以照顾到一个索引 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n),令 a i = a i + 1 a_i=a_i+1 ai=ai+1。
问经过若干次操作后,能否使 ∏ i = 1 n a i m o d k = 0 \prod_{i=1}^n a_i \mod k = 0 ∏i=1naimodk=0。
题解
观察发现, k k k 的取值范围是 2 ∼ 5 2\sim 5 2∼5,除去 4 = 2 × 2 4 = 2\times 2 4=2×2 以外,其他数分解质因数都还是本身。
分类讨论:
-
k ∈ { 2 , 3 , 5 } k \in \{2,3,5\} k∈{2,3,5}:找到一个 d m i n ( a i + d i ≡ 0 ( m o d k ) ( 1 ≤ i ≤ n ) ) d_{min}(a_i+d_i\equiv 0 (\mod k)(1\le i\le n)) dmin(ai+di≡0(modk)(1≤i≤n)),也就是找到最小的一个大于 a i a_i ai 且与它最近的 k k k 的倍数与 a i a_i ai 的差,则可知 d m i n = min ( ⌈ a i k ⌉ k − a i ) d_{min}= \min\left(\lceil\frac{a_i}{k}\rceil k - a_i\right) dmin=min(⌈kai⌉k−ai)
-
k = 4 k = 4 k=4:有一下几种情况:
- 存在 a i m o d 4 = 0 ( 1 ≤ i ≤ n ) a_i\mod 4=0(1\le i\le n) aimod4=0(1≤i≤n),答案为 0 0 0
- 存在 a i m o d 2 = 0 , a j m o d 2 = 0 ( 1 ≤ i , j ≤ n , i ≠ j ) a_i\mod 2 = 0,a_j\mod 2=0(1\le i,j\le n,i\ne j) aimod2=0,ajmod2=0(1≤i,j≤n,i=j),答案为 0 0 0
- 仅存在一个 a i m o d 2 = 0 ( 1 ≤ i ≤ n ) a_i\mod 2=0(1\le i\le n) aimod2=0(1≤i≤n),再凑出一个偶数即可满足情况2,单位 1 1 1
- 存在 a i m o d 4 = 1 ( 1 ≤ i ≤ n ) a_i\mod 4=1(1\le i\le n) aimod4=1(1≤i≤n),答案为 1 1 1
- 全部是奇数,则凑出两个偶数即可满足情况2,答案为 2 2 2
分别用变量记录各自所需的 a i a_i ai 出现的次数即可,注意判断顺序,答案小的优先。
代码如下:
#include <bits/stdc++.h>
#define endl '\n'
#define file(FILENAME) \
freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout)
#define CLOSE \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int t, n, k, a;
int main() {
CLOSE;
cin >> t;
while (t--) {
cin >> n >> k;
int ans = 0, p1 = 0, p2 = 0, p3 = 0, p4 = 0;
for (int i = 1; i <= n; i++) {
cin >> a;
if (k != 4) ans = min(ans, (a / k + (a % k != 0)) * k - a); //k不是4,找d的最小值
else { //k = 4
if (a % 4 == 0) p1++; //记录情况1
else if (a % 2 == 0) p2++; //记录情况2、3
else if ((a + 1) % 4 == 0) p3++; //记录情况4
else p4++; //记录情况5
}
}
if (k != 4) cout << ans << endl;
else {
if (p1 > 0) cout << 0 << endl; //判断情况1
else if (p2 >= 2) cout << 0 << endl; //判断情况2
else if (p2 == 1) cout << 1 << endl; //判断情况3
else if (p3 > 0) cout << 1 << endl; //判断情况4
else cout << 2 << endl; //判断情况5
}
}
return 0;
}
D. In Love
题意描述
现在给出 q q q 次操作:
- + l r +\ l\ r + l r:在不去重集合中加入片段 ( l , r ) (l, r) (l,r)
- − l r -\ l\ r − l r:在不去重集合中删除一个片段 ( l , r ) (l, r) (l,r)
每次操作后,判断集合中是否存在一对片段不重合。
题解
我们只需要判断相隔最小的两个片段是否重合即可。如果不重合,则至少存在一对;如果重合,那么其他的片段也必然重合。
而一对不重合的片段 ( l 1 , r 1 ) (l_1,r_1) (l1,r1) 和 ( l 2 , r 2 ) (l_2, r_2) (l2,r2) 满足 r 1 < l 1 r_1<l_1 r1<l1。
所以,我们可以分开存储左端点 l l l 和右端点 r r r。如果 r m i n < l m a x r_{min}<l_{max} rmin<lmax ,则存在;反之,不存在。
能够快速排序和删除,还要满足不去重的特性,multiset
是最佳的选择。
注意,
l
l
l 每次取最大值,而multiset
默认升序排序,但我们知道,
∀
a
<
b
<
0
,
−
a
>
−
b
\forall a<b<0,-a>-b
∀a<b<0,−a>−b,可以保存
−
l
-l
−l 的值,删除使用时取相反数即可。
代码如下:
#include <bits/stdc++.h>
#define endl '\n'
#define file(FILENAME) \
freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout)
#define CLOSE \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, l, r;
map<int, int> visl, visr;
char k;
multiset<int> L, R;
int main() {
CLOSE;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> k >> l >> r;
if (k == '+') {
L.insert(-l);
visl[l]++;
R.insert(r);
visr[r]++;
} else {
int t = L.erase(-l);
t = R.erase(r);
visl[l]--, visr[r]--;
if (visl[l]) L.insert(-l);
if (visr[r]) R.insert(r);
}
cout << (-(*L.begin()) > *R.begin() ? "YES\n" : "NO\n");
}
return 0;
}
E. Look Back
题目描述
输入 t t t 组数据。
给定一个数组 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an。
可进行操作:找到一个索引 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n),令 a i = a i × 2 a_i = a_i\times 2 ai=ai×2。
问最少进行多少次操作,能使数组成为一个非递减数组。
题解
L o g i Log_i Logi :表示 a i a_i ai 需要乘上 2 i 2^i 2i(即需要操作 i i i 次), L o g i ∈ N Log_i\in \N Logi∈N。
从左往右对 a i ( 1 ≤ i ≤ n ) a_i(1\le i\le n) ai(1≤i≤n) 操作。
先令 L o g i = L o g i − 1 ( 2 ≤ i ≤ n ) Log_i=Log_{i-1}(2\le i\le n) Logi=Logi−1(2≤i≤n)。
分类讨论:
-
a i > a i − 1 a_i>a_{i - 1} ai>ai−1:说明 L o g i Log_i Logi 过大, 要让操作次数尽可能小且仍要满足条件,则设 L o g i Log_i Logi 要减去 k ( k ∈ N ) k(k\in \N) k(k∈N)
a i ≥ a i − 1 × 2 k 2 k ≥ a i a i − 1 log 2 ( 2 k ) ≥ log 2 ( a i a i − 1 ) k ≥ log 2 ( a i a i − 1 ) ∵ k ∈ N , a i × 2 L o g i − 1 − k ≥ a i − 1 , ∴ k = ⌊ log 2 ( a i a i − 1 ) ⌋ \begin{aligned} a_i&\ge a_{i - 1}\times 2^{k} \\ 2^{k}&\ge \frac{a_{i}}{a_{i-1}} \\ \log_2\left(2^{k}\right)&\ge \log_2\left(\frac{a_{i}}{a_{i-1}}\right) \\ k&\ge \log_2\left(\frac{a_{i}}{a_{i-1}}\right) \\ \because k\in \N,a_i\times 2^{Log_{i - 1}-k}\ge a_{i - 1},\\ \therefore k&= \left\lfloor\log_2\left(\frac{a_{i}}{a_{i-1}}\right)\right\rfloor \end{aligned} ai2klog2(2k)k∵k∈N,ai×2Logi−1−k≥ai−1,∴k≥ai−1×2k≥ai−1ai≥log2(ai−1ai)≥log2(ai−1ai)=⌊log2(ai−1ai)⌋
注意, L o g i Log_i Logi 不能为负数,所以应是
L o g i = max ( 0 , L o g i − 1 − ⌊ log 2 ( a i a i − 1 ) ⌋ ) Log_i=\max\left(0,Log_{i-1}-\left\lfloor\log_2\left(\frac{a_{i}}{a_{i-1}}\right)\right\rfloor\right) Logi=max(0,Logi−1−⌊log2(ai−1ai)⌋) -
a i < a i − 1 a_i<a_i-1 ai<ai−1:同理,设 L o g i Log_i Logi 要加上 k ( k ∈ N ) k(k\in \N) k(k∈N)
a i × 2 k ≥ a i − 1 2 k ≥ a i − 1 a i log 2 ( 2 k ) ≥ log 2 ( a i − 1 a i ) k ≥ log 2 ( a i − 1 a i ) ∵ k ∈ N , a i × 2 L o g i − 1 + k ≥ a i − 1 , ∴ k = ⌈ log 2 ( a i − 1 a i ) ⌉ \begin{aligned} a_i \times 2^{k} &\ge a_{i - 1} \\ 2^{k}&\ge \frac{a_{i - 1}}{a_{i}} \\ \log_2\left(2^{k}\right)&\ge \log_2\left(\frac{a_{i-1}}{a_{i}}\right) \\ k&\ge \log_2\left(\frac{a_{i-1}}{a_{i}}\right) \\ \because k\in \N,a_i\times 2^{Log_{i - 1}+k}\ge a_{i - 1},\\ \therefore k&= \left\lceil\log_2\left(\frac{a_{i-1}}{a_{i}}\right)\right\rceil \end{aligned} ai×2k2klog2(2k)k∵k∈N,ai×2Logi−1+k≥ai−1,∴k≥ai−1≥aiai−1≥log2(aiai−1)≥log2(aiai−1)=⌈log2(aiai−1)⌉
由此可得
L o g i = max ( 0 , L o g i − 1 + ⌈ log 2 ( a i − 1 a i ) ⌉ ) Log_i=\max\left(0,Log_{i-1}+\left\lceil\log_2\left(\frac{a_{i-1}}{a_{i}}\right)\right\rceil\right) Logi=max(0,Logi−1+⌈log2(aiai−1)⌉)
∑ i = 1 n L o g i \sum_{i=1}^nLog_i ∑i=1nLogi 就是操作总次数。
代码如下:
#include <bits/stdc++.h>
#define endl '\n'
#define file(FILENAME) \
freopen(FILENAME ".in", "r", stdin), freopen(FILENAME ".out", "w", stdout)
#define CLOSE \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0)
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int t, n;
ll a[N], Log[N];
int main() {
CLOSE;
cin >> t;
while (t--) {
cin >> n;
ll ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (i > 1) {
if (a[i] > a[i - 1])
Log[i] =
max((ll)0, Log[i - 1] -
(ll)floor(log2(a[i] * 1.0 / a[i - 1])));
else
Log[i] =
max((ll)0,
Log[i - 1] + (ll)ceil(log2(a[i - 1] * 1.0 / a[i])));
ans += Log[i];
}
}
cout << ans << endl;
}
return 0;
}