目录
1 AC情况
A | B | C | D | E | F1 交互题 \ \colorbox{skyblue}{\color{white}\small\texttt{交互题}} 交互题 | F2 | G |
---|---|---|---|---|---|---|---|
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 | Accepted \color{green}\texttt{Accepted} Accepted | - | - |
2 题解
A. Do Not Be Distracted!
题目描述
输入 t t t 组数据。
n n n 项任务,不同任务用不同大写字母表示。每天只能做一项任务,如果一项任务分成了多次做,且中间又做了其他的任务,则会被怀疑。求是否会被怀疑。
题解
桶标记是否任务做过。如果做过,而且有间隔,则会被怀疑。
部分代码(标记间隔)如下:
vis[s[0]] = 1;
for (int i = 1; i < n; i++) {
if (vis[s[i]] && s[i] != s[i - 1]) {
flag = 1;
break;
}
vis[s[i]] = 1;
}
B. Ordinary Numbers
题目描述
输入 t t t 组数据。
如果一个正整数 n n n 在十进制符号中的所有数位都相同,我们就称它为普通数。
对于给定的数 n n n ,求从 1 1 1 到 n n n 的数中普通数的个数。
题解
打表求出 1 ∼ 1 0 9 1\sim 10^9 1∼109 所有的普通数。
不同的是,如果每个数遍历检查实在太慢,可以先记录 1 1 1, 2 2 2,…, 9 9 9;然后在它们后面加一位,记录 11 11 11, 22 22 22,…, 99 99 99;重复,记录 111 111 111, 222 222 222,…, 999 999 999;逐次加位数。
查找时只要累计多少个普通数小于等于 n n n 即可。
部分代码(打表)如下:
void init() {
for (int i = 1; i <= 9; i++) a[++cnt] = i;
for (int i = 1; a[i] < N; i++) {
a[++cnt] = a[i] * 10 + a[i] % 10;
}
}
C. Not Adjacent Matrix
题目描述
输入 t t t 组数据。
把 1 ∼ n 2 1\sim n^2 1∼n2 填写进一个 n 2 n^2 n2 的矩阵,使得一个单元的相邻的单元(上、下、左、右挨着的单元)的数值与其不相邻(数值相邻是指两个数绝对值等于一)。无法填写合法矩阵,输出
-1
。
题解
方法一:
既然上下左右不合法,那么对角线合法。样例:
1
4
8
6
2
5
9
7
3
\def\arraystretch{1.2} \begin{array}{|c|c|c|}\hline 1 & 4 & 8 \\ \hline 6 & 2 & 5 \\ \hline 9 & 7 & 3 \\ \hline \end{array}
169427853
部分代码(构造矩阵)如下:
void func(int n) {
int cnt = 1;
for (int i = 1; i <= n; i++)
ans[i][i] = cnt, cnt++;
for (int i = 1; i < n; i++) {
for (int j = 1; j <= n - i; j++)
ans[j][j + i] = cnt, cnt++;
for (int j = 1; j <= n - i; j++)
ans[i + j][j] = cnt, cnt++;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << ans[i][j] << " ";
}
cout << endl;
}
}
方法2:
先填奇数,再填偶数。 样例:
1
3
5
7
9
2
4
6
8
\def\arraystretch{1.2} \begin{array}{|c|c|c|}\hline 1 & 3 & 5 \\ \hline 7 & 9 & 2 \\ \hline 4 & 6 & 8 \\ \hline \end{array}
174396528
部分代码(构造矩阵)如下:
void func(int n) {
int cnt = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << cnt << " ";
cnt += 2;
if (cnt > n * n)
cnt = 2;
}
cout << endl;
}
}
D. Same Differences
题目描述
输入 t t t 组数据。
一个由 n n n 个整数组成的数组 a a a ,数出索引 ( i , j ) (i, j) (i,j) 中有多少对索引 i < j i < j i<j 和 a j − a i = j − i a_j - a_i = j - i aj−ai=j−i 。
题解
合法的索引必然满足 a i − i = a j − j a_i-i=a_j-j ai−i=aj−j。因此,只要记录 ( a i − i ) \left(a_i-i\right) (ai−i) 的值,值相同的就能组成索引。累加索引个数。
部分代码(操作过程)如下:
map<long long, long long> mp;
for (int i = 1; i <= n; i++) {
cin >> a;
ans += mp[a - i];
mp[a - i]++;
}
E. Arranging The Sheep
题目描述
输入 t t t 组数据。
一个长度为 n n n、由
.
和*
组成的字符串。其中.
表示空格,*
表示物体,可以向左或向右移。求用最少的步数让所有的*
紧靠在一起。
题解
向最中间的 *
靠拢的总步数是最少的。如果向左或向右靠拢都会存在步数重叠。
记录步数即可。
部分代码(找中间和记录过程)如下:
ll mid = 0, flag = 0, cnt = 0;
for (ll i = 0; i < n; i++)
if (s[i] == '*') cnt++;
ll find = 0;
for (ll i = 0; i < n; i++){
if (s[i] == '*') find++;
if (find == cnt / 2 + 1) {
mid = i;
break;
}
}
flag = mid, ans = 0;
for (ll i = mid - 1; i >= 0; i--) {
if (s[i] == '*') {
ans += flag - i - 1;
flag--;
}
}
flag = mid;
for (ll i = mid + 1; i < n; i++) {
if (s[i] == '*') {
ans += i - flag - 1;
flag++;
}
}
F1. Guess the K-th Zero (Easy version)
Problem - F1 - Codeforces|Guess the K-th Zero (Easy version) - 洛谷
题目描述
这是一道交互题。
输入 n , t ( t = 1 ) , k n,t(t=1),k n,t(t=1),k, n n n 表示有一个由 1 1 1 和 0 0 0 构成的隐藏数组, k k k 表示你要回答第 k k k 个 0 0 0 的位置。
你可以向交互器提问不超过 20 20 20 个问题,格式如下:
- ? l r ?\ l\ r ? l r:交互器将回答你的问题一个数 n u m num num,表示 ∑ i = l r a i \sum_{i=l}^ra_i ∑i=lrai。
每次提问输出后加上代码
cout.flush()
,用于清理缓冲区来及时向交互器提问。最后输出格式如下:
- ! x !\ x ! x: x x x 表示第 k k k 个 0 0 0 的位置。
题解
考虑二分提问区间。
询问 [ l , m i d ] [l,mid] [l,mid],答案为 a n s ans ans,区间元素个数为 x ( x = m i d − l + 1 ) x(x=mid-l+1) x(x=mid−l+1),考虑:
- x − a n s ≥ k x-ans\ge k x−ans≥k:说明第 k k k 个 0 0 0 就在 [ l , m i d ] [l,mid] [l,mid] 中, r = m i d r=mid r=mid;
- x − a n s < k x - ans<k x−ans<k:说明第 k k k 个 0 0 0 在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 中,则 k k k 更新为在新区间中的次序,即 k = k − ( x − a n s ) k = k - (x-ans) k=k−(x−ans), l = m i d + 1 l = mid+1 l=mid+1。
部分代码(主函数)如下:
int main() {
cin >> n >> t >> k;
int l = 1, r = n, cnt = 20;
while (l < r && cnt--) {
int mid = (l + r) >> 1, ans;
cout << "? " << l << " " << mid << endl;
cout.flush();
cin >> ans;
if (mid - l + 1 - ans >= k) {
r = mid;
} else {
k -= mid - l + 1 - ans;
l = mid + 1;
}
}
cout << "! " << l;
return 0;
}
G. To Go Or Not To Go?
Problem - G - Codeforces|To Go Or Not To Go? - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
输入 n , m , w n,m,w n,m,w。 n , m n,m n,m 表示地图尺寸, w w w 表示相邻单元格之间移动所用的时间。
每个单元格有三种情况:
- − 1 -1 −1:表示无法通过单元格;
- 0 0 0:表示可通过,移动时间为 w w w;
- x ( x ≠ 0 ) x(x\ne 0) x(x=0): 表示是一个入口,可以选择当成一个普通单元,移动时间为 w w w;也可以选择当成一个传送门,可以传送到任意一个其他的传送门,所用时间为起点和终点传送门 x x x 之和。
求用最少的时间从 ( 1 , 1 ) (1,1) (1,1) 到 ( n , m ) (n,m) (n,m)。
题解
不难发现,传送门最多只走一次。
第一遍 bfs:求从 ( 1 , 1 ) (1,1) (1,1) 到各个传送门的时间;
第二遍 bfs:求从 ( n , m ) (n,m) (n,m) 到各个传送门的时间。
求出如果走传送门,需要的最短时间。
第三遍 bfs:求不走传送门的时间(可能不存在路径)。
比较走和不走传送门的时间,求最小值。